mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-08 23:23:23 -06:00
9d05583c26
Despite being a known quantity, autotools is so overkill for our needs, so let's drop it and replace it with a much simpler and easy to customize system.
344 lines
11 KiB
Python
344 lines
11 KiB
Python
#
|
|
# Copyright (C) 2007 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.
|
|
#
|
|
|
|
from gi.repository import GObject
|
|
|
|
import logging
|
|
import os
|
|
import glob
|
|
|
|
import dbus
|
|
|
|
from virtManager.baseclass import vmmGObject
|
|
from virtManager.netdev import vmmNetDevice
|
|
from virtManager.mediadev import vmmMediaDevice
|
|
|
|
_hal_helper = None
|
|
|
|
def get_hal_helper(init=True):
|
|
global _hal_helper
|
|
if not _hal_helper and init:
|
|
_hal_helper = vmmHalHelper()
|
|
return _hal_helper
|
|
|
|
def cleanup():
|
|
global _hal_helper
|
|
if _hal_helper:
|
|
_hal_helper.cleanup()
|
|
_hal_helper = None
|
|
|
|
def get_net_bridge_owner(name_ignore, sysfspath):
|
|
# Now magic to determine if the device is part of a bridge
|
|
brportpath = os.path.join(sysfspath, "brport")
|
|
|
|
try:
|
|
if os.path.exists(brportpath):
|
|
brlinkpath = os.path.join(brportpath, "bridge")
|
|
dest = os.readlink(brlinkpath)
|
|
(ignore, bridge) = os.path.split(dest)
|
|
return bridge
|
|
except:
|
|
logging.exception("Unable to determine if device is shared")
|
|
|
|
return None
|
|
|
|
def get_net_mac_address(name_ignore, sysfspath):
|
|
mac = None
|
|
addrpath = sysfspath + "/address"
|
|
if os.path.exists(addrpath):
|
|
df = open(addrpath, 'r')
|
|
mac = df.readline().strip(" \n\t")
|
|
df.close()
|
|
return mac
|
|
|
|
def get_bonding_masters():
|
|
masters = []
|
|
if os.path.exists("/sys/class/net/bonding_masters"):
|
|
f = open("/sys/class/net/bonding_masters")
|
|
while True:
|
|
rline = f.readline()
|
|
if not rline:
|
|
break
|
|
if rline == "\x00":
|
|
continue
|
|
rline = rline.strip("\n\t")
|
|
masters = rline[:].split(' ')
|
|
return masters
|
|
|
|
def is_net_bonding_slave(name_ignore, sysfspath):
|
|
masterpath = sysfspath + "/master"
|
|
if os.path.exists(masterpath):
|
|
return True
|
|
return False
|
|
|
|
class vmmHalHelper(vmmGObject):
|
|
__gsignals__ = {
|
|
"netdev-added": (GObject.SignalFlags.RUN_FIRST, None, [object]),
|
|
"optical-added": (GObject.SignalFlags.RUN_FIRST, None, [object]),
|
|
"optical-media-added": (GObject.SignalFlags.RUN_FIRST, None, [str, str, str]),
|
|
"device-removed": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
}
|
|
|
|
def __init__(self):
|
|
vmmGObject.__init__(self)
|
|
|
|
self.bus = None
|
|
self.hal_iface = None
|
|
self.sigs = []
|
|
|
|
# Error message we encountered when initializing
|
|
self.startup_error = None
|
|
|
|
self._dbus_connect()
|
|
|
|
def _cleanup(self):
|
|
self.bus = None
|
|
self.hal_iface = None
|
|
|
|
for sig in self.sigs:
|
|
sig.remove()
|
|
self.sigs = []
|
|
|
|
def get_init_error(self):
|
|
return self.startup_error
|
|
|
|
def _dbus_connect(self):
|
|
# Probe for network devices
|
|
try:
|
|
# Get a connection to the SYSTEM bus
|
|
self.bus = dbus.SystemBus()
|
|
# Get a handle to the HAL service
|
|
hal_object = self.bus.get_object('org.freedesktop.Hal',
|
|
'/org/freedesktop/Hal/Manager')
|
|
self.hal_iface = dbus.Interface(hal_object,
|
|
'org.freedesktop.Hal.Manager')
|
|
|
|
|
|
# Track device add/removes so we can detect newly inserted CD media
|
|
self.sigs.append(
|
|
self.hal_iface.connect_to_signal("DeviceAdded",
|
|
self._device_added))
|
|
self.sigs.append(
|
|
self.hal_iface.connect_to_signal("DeviceRemoved",
|
|
self._device_removed))
|
|
except Exception, e:
|
|
logging.error("Unable to connect to HAL to list network "
|
|
"devices: " + str(e))
|
|
self.startup_error = str(e)
|
|
|
|
def connect(self, name, callback, *args):
|
|
handle_id = vmmGObject.connect(self, name, callback, *args)
|
|
|
|
if name == "netdev-added":
|
|
self.populate_netdevs()
|
|
|
|
elif name == "optical-added":
|
|
self.populate_opt_media()
|
|
|
|
return handle_id
|
|
|
|
|
|
##################
|
|
# Helper methods #
|
|
##################
|
|
|
|
def dbus_dev_lookup(self, halpath):
|
|
obj = self.bus.get_object("org.freedesktop.Hal", halpath)
|
|
objif = dbus.Interface(obj, "org.freedesktop.Hal.Device")
|
|
return objif
|
|
|
|
def is_cdrom_media(self, halpath):
|
|
obj = self.dbus_dev_lookup(halpath)
|
|
return bool(obj.QueryCapability("volume") and
|
|
obj.GetPropertyBoolean("volume.is_disc") and
|
|
obj.GetPropertyBoolean("volume.disc.has_data"))
|
|
|
|
def is_cdrom(self, halpath):
|
|
obj = self.dbus_dev_lookup(halpath)
|
|
return bool(obj.QueryCapability("storage.cdrom"))
|
|
|
|
def is_netdev(self, halpath):
|
|
obj = self.dbus_dev_lookup(halpath)
|
|
return bool(obj.QueryCapability("net"))
|
|
|
|
|
|
#############################
|
|
# Initial device population #
|
|
#############################
|
|
|
|
def populate_opt_media(self):
|
|
for path in self.hal_iface.FindDeviceByCapability("storage.cdrom"):
|
|
# Make sure we only populate CDROM devs
|
|
if not self.is_cdrom(path):
|
|
continue
|
|
|
|
devnode, media_label, media_hal_path = self._fetch_cdrom_info(path)
|
|
self.add_optical_dev(str(devnode), str(path), media_label,
|
|
media_hal_path)
|
|
|
|
|
|
def populate_netdevs(self):
|
|
bondMasters = get_bonding_masters()
|
|
|
|
for bond in bondMasters:
|
|
sysfspath = "/sys/class/net/" + bond
|
|
mac = get_net_mac_address(bond, sysfspath)
|
|
if mac:
|
|
self._net_device_added(bond, mac, sysfspath)
|
|
|
|
# Add any associated VLANs
|
|
self._net_tag_device_added(bond, sysfspath)
|
|
|
|
# Find info about all current present physical net devices
|
|
for path in self.hal_iface.FindDeviceByCapability("net"):
|
|
self._net_phys_device_added(path)
|
|
|
|
|
|
#############################
|
|
# Device callback listeners #
|
|
#############################
|
|
|
|
def _device_added(self, path):
|
|
if self.is_cdrom_media(path):
|
|
self._optical_media_added(path)
|
|
elif self.is_cdrom(path):
|
|
self._optical_added(path)
|
|
elif self.is_netdev(path):
|
|
self._net_phys_device_added(path)
|
|
|
|
def _device_removed(self, path):
|
|
self.emit("device-removed", str(path))
|
|
|
|
def add_optical_dev(self, devpath, halpath, media_label, media_hal_path):
|
|
obj = vmmMediaDevice(devpath, halpath, bool(media_label),
|
|
media_label, media_hal_path)
|
|
obj.set_hal_media_signals(self)
|
|
self.emit("optical-added", obj)
|
|
|
|
def _optical_added(self, halpath):
|
|
devpath, media_label, media_hal_path = self._fetch_cdrom_info(halpath)
|
|
self.add_optical_dev(devpath, halpath, media_label, media_hal_path)
|
|
|
|
def _optical_media_added(self, media_hal_path):
|
|
media_label, devpath = self._fetch_media_info(media_hal_path)
|
|
|
|
self.emit("optical-media-added", devpath, media_label, media_hal_path)
|
|
|
|
def _net_phys_device_added(self, halpath):
|
|
dbusobj = self.dbus_dev_lookup(halpath)
|
|
|
|
name = dbusobj.GetPropertyString("net.interface")
|
|
mac = dbusobj.GetPropertyString("net.address")
|
|
|
|
# HAL gives back paths to like:
|
|
# /sys/devices/pci0000:00/0000:00:1e.0/0000:01:00.0/net/eth0
|
|
# which doesn't work so well - we want this:
|
|
sysfspath = "/sys/class/net/" + name
|
|
|
|
# If running a device in bridged mode, there's a reasonable
|
|
# chance that the actual ethernet device has been renamed to
|
|
# something else. ethN -> pethN
|
|
psysfspath = sysfspath[0:len(sysfspath) - len(name)] + "p" + name
|
|
if os.path.exists(psysfspath):
|
|
logging.debug("Device %s named to p%s", name, name)
|
|
name = "p" + name
|
|
sysfspath = psysfspath
|
|
|
|
# Ignore devices that are slaves of a bond
|
|
if is_net_bonding_slave(name, sysfspath):
|
|
logging.debug("Skipping device %s in bonding slave", name)
|
|
return
|
|
|
|
# Add the main NIC
|
|
self._net_device_added(name, mac, sysfspath, halpath)
|
|
|
|
# Add any associated VLANs
|
|
self._net_tag_device_added(name, sysfspath)
|
|
|
|
def _net_tag_device_added(self, name, sysfspath):
|
|
for vlanpath in glob.glob(sysfspath + ".*"):
|
|
if os.path.exists(vlanpath):
|
|
logging.debug("Process VLAN %s", vlanpath)
|
|
vlanmac = get_net_mac_address(name, vlanpath)
|
|
if vlanmac:
|
|
(ignore, vlanname) = os.path.split(vlanpath)
|
|
|
|
# If running a device in bridged mode, there's areasonable
|
|
# chance that the actual ethernet device has beenrenamed to
|
|
# something else. ethN -> pethN
|
|
pvlanpath = (vlanpath[0:len(vlanpath) - len(vlanname)] +
|
|
"p" + vlanname)
|
|
if os.path.exists(pvlanpath):
|
|
logging.debug("Device %s named to p%s",
|
|
vlanname, vlanname)
|
|
vlanname = "p" + vlanname
|
|
vlanpath = pvlanpath
|
|
self._net_device_added(vlanname, vlanmac, vlanpath)
|
|
|
|
def _net_device_added(self, name, mac, sysfspath, halpath=None):
|
|
bridge = get_net_bridge_owner(name, sysfspath)
|
|
shared = False
|
|
if bridge is not None:
|
|
shared = True
|
|
|
|
dev = vmmNetDevice(name, mac, shared, bridge, halpath)
|
|
self.emit("netdev-added", dev)
|
|
|
|
|
|
######################
|
|
# CDROM info methods #
|
|
######################
|
|
|
|
def _fetch_media_info(self, halpath):
|
|
label = None
|
|
devnode = None
|
|
|
|
volif = self.dbus_dev_lookup(halpath)
|
|
|
|
devnode = volif.GetProperty("block.device")
|
|
label = volif.GetProperty("volume.label")
|
|
if not label:
|
|
label = devnode
|
|
|
|
return (label and str(label), devnode and str(devnode))
|
|
|
|
def _fetch_cdrom_info(self, halpath):
|
|
devif = self.dbus_dev_lookup(halpath)
|
|
|
|
devnode = devif.GetProperty("block.device")
|
|
media_label = None
|
|
media_hal_path = None
|
|
|
|
if devnode:
|
|
media_label, media_hal_path = self._find_media_for_devpath(devnode)
|
|
|
|
return (devnode and str(devnode), media_label, media_hal_path)
|
|
|
|
def _find_media_for_devpath(self, devpath):
|
|
for path in self.hal_iface.FindDeviceByCapability("volume"):
|
|
if not self.is_cdrom_media(path):
|
|
continue
|
|
|
|
label, devnode = self._fetch_media_info(path)
|
|
|
|
if devnode == devpath:
|
|
return (label, path)
|
|
|
|
return None, None
|