Cole Robinson wrote: > Add VirtualHostDevice classes for build domain 'hostdev' device xml, > along with associated tests. > > This is currently incomplete: I'm not entirely clear on how a managed > vs. unmanaged device interacts with HostDeviceAttach/Reset, and if there > are differences in whats applicable for USB vs. PCI devices. The libvirt > API for all this hasn't officially landed yet either so this is blocked > until then. > Okay, I think this updated version should be sufficient now. Thanks, Cole
# HG changeset patch # User Cole Robinson <crobinso@xxxxxxxxxx> # Node ID 17e1c491f429e984e96fb61ca6ae17be4d1e285c # Parent daf6b88c04485828bd8e8edd68e5a62ef38b4be6 Add VirtualHostDevice classes, for attaching physical host devices to a guest. diff -r daf6b88c0448 -r 17e1c491f429 tests/nodedev-xml/devxml/pcidev.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/nodedev-xml/devxml/pcidev.xml Mon Mar 02 10:40:55 2009 -0500 @@ -0,0 +1,5 @@ + <hostdev mode='subsystem' type='pci' managed='yes'> + <source> + <address domain='0' bus='21' slot='0' function='4'/> + </source> + </hostdev> diff -r daf6b88c0448 -r 17e1c491f429 tests/nodedev-xml/devxml/usbdev1.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/nodedev-xml/devxml/usbdev1.xml Mon Mar 02 10:40:55 2009 -0500 @@ -0,0 +1,6 @@ + <hostdev mode='subsystem' type='usb' managed='yes'> + <source> + <vendor id='0x0781'/> + <product id='0x5151'/> + </source> + </hostdev> diff -r daf6b88c0448 -r 17e1c491f429 tests/nodedev-xml/devxml/usbdev2.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/nodedev-xml/devxml/usbdev2.xml Mon Mar 02 10:40:55 2009 -0500 @@ -0,0 +1,5 @@ + <hostdev mode='subsystem' type='usb' managed='yes'> + <source> + <address bus='1' device='4'/> + </source> + </hostdev> diff -r daf6b88c0448 -r 17e1c491f429 tests/nodedev.py --- a/tests/nodedev.py Mon Mar 02 10:10:18 2009 -0500 +++ b/tests/nodedev.py Mon Mar 02 10:40:55 2009 -0500 @@ -14,9 +14,11 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA. +import tests import os.path import unittest import virtinst.NodeDeviceParser as nodeparse +from virtinst import VirtualHostDevice import libvirt conn = libvirt.open("test:///default") @@ -33,6 +35,14 @@ for attr in vals.keys(): self.assertEqual(vals[attr], getattr(dev, attr)) + def _testNode2DeviceCompare(self, nodefile, devfile, nodedev=None): + devfile = os.path.join("tests/nodedev-xml/devxml", devfile) + if not nodedev: + nodedev = self._nodeDevFromFile(nodefile) + + dev = VirtualHostDevice.device_from_node(conn, nodedev=nodedev) + tests.diff_compare(dev.get_xml_config(), devfile) + def testSystemDevice(self): filename = "system.xml" vals = {"hw_vendor": "LENOVO", "hw_version": "ThinkPad T61", @@ -150,5 +160,37 @@ "type": "disk"} self._testCompare(filename, vals) + + # NodeDevice 2 Device XML tests + def testNodeDev2USB1(self): + nodefile = "usbdev1.xml" + devfile = "usbdev1.xml" + self._testNode2DeviceCompare(nodefile, devfile) + + def testNodeDev2USB2(self): + nodefile = "usbdev1.xml" + devfile = "usbdev2.xml" + nodedev = self._nodeDevFromFile(nodefile) + + # Force xml building to use bus, addr + nodedev.product_id = None + nodedev.vendor_id = None + + self._testNode2DeviceCompare(nodefile, devfile, nodedev=nodedev) + + def testNodeDev2PCI(self): + nodefile = "pci1.xml" + devfile = "pcidev.xml" + self._testNode2DeviceCompare(nodefile, devfile) + + def testNodeDevFail(self): + nodefile = "usbbus.xml" + devfile = "" + + # This should exist, since usbbus is not a valid device to + # pass to a guest. + self.assertRaises(ValueError, + self._testNode2DeviceCompare, nodefile, devfile) + if __name__ == "__main__": unittest.main() diff -r daf6b88c0448 -r 17e1c491f429 virtinst/Guest.py --- a/virtinst/Guest.py Mon Mar 02 10:10:18 2009 -0500 +++ b/virtinst/Guest.py Mon Mar 02 10:40:55 2009 -0500 @@ -79,6 +79,7 @@ self.disks = [] self.nics = [] self.sound_devs = [] + self.hostdevs = [] # Device lists to use/alter during install process self._install_disks = [] @@ -413,6 +414,12 @@ xml = _util.xml_append(xml, sound_dev.get_xml_config()) return xml + def _get_hostdev_xml(self): + xml = "" + for hostdev in self.hostdevs: + xml = _util.xml_append(xml, hostdev.get_xml_config()) + return xml + def _get_device_xml(self, install=True): xml = "" @@ -421,6 +428,7 @@ xml = _util.xml_append(xml, self._get_input_xml()) xml = _util.xml_append(xml, self._get_graphics_xml()) xml = _util.xml_append(xml, self._get_sound_xml()) + xml = _util.xml_append(xml, self._get_hostdev_xml()) return xml def _get_features_xml(self): @@ -582,12 +590,14 @@ if self._installer.install_disk is not None: self._install_disks.append(self._installer.install_disk) - def _create_devices(self,progresscb): + def _create_devices(self, progresscb): """Ensure that devices are setup""" for disk in self._install_disks: disk.setup(progresscb) for nic in self._install_nics: nic.setup(self.conn) + for hostdev in self.hostdevs: + hostdev.setup() def _do_install(self, consolecb, meter, removeOld=False, wait=True): vm = None diff -r daf6b88c0448 -r 17e1c491f429 virtinst/NodeDeviceParser.py --- a/virtinst/NodeDeviceParser.py Mon Mar 02 10:10:18 2009 -0500 +++ b/virtinst/NodeDeviceParser.py Mon Mar 02 10:40:55 2009 -0500 @@ -330,6 +330,25 @@ return True +def is_pci_detach_capable(conn): + """ + Check if the passed libvirt connection support pci device Detach/Reset + + @param conn: Connection to check + @type conn: libvirt.virConnect + + @rtype: C{bool} + """ + if not conn: + return False + if not isinstance(conn, libvirt.virConnect): + raise ValueError(_("'conn' must be a virConnect instance.")) + + if dir(libvirt).count("virNodeDeviceDettach") == 0: + return False + + return True + def lookupNodeName(conn, name): """ Convert the passed libvirt node device name to a NodeDevice diff -r daf6b88c0448 -r 17e1c491f429 virtinst/VirtualHostDevice.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/virtinst/VirtualHostDevice.py Mon Mar 02 10:40:55 2009 -0500 @@ -0,0 +1,209 @@ +# +# Copyright 2009 Red Hat, Inc. +# Cole Robinson <crobinso@xxxxxxxxxx> +# +# 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 VirtualDevice +import NodeDeviceParser +import logging +from virtinst import _virtinst as _ + +class VirtualHostDevice(VirtualDevice.VirtualDevice): + + def device_from_node(conn, name=None, nodedev=None): + """ + Convert the passed libvirt node device name to a VirtualHostDevice + instance, with proper error reporting. + + @param conn: libvirt.virConnect instance to perform the lookup on + @param name: libvirt node device name to lookup + + @rtype: L{virtinst.VirtualHostDevice} instance + """ + + if not name and not nodedev: + raise ValueError(_("'name' or 'nodedev' required.")) + + if nodedev: + nodeinst = nodedev + else: + nodeinst = NodeDeviceParser.lookupNodeName(conn, name) + + if isinstance(nodeinst, NodeDeviceParser.PCIDevice): + return VirtualHostDevicePCI(conn, nodedev=nodeinst) + elif isinstance(nodeinst, NodeDeviceParser.USBDevice): + return VirtualHostDeviceUSB(conn, nodedev=nodeinst) + elif isinstance(nodeinst, NodeDeviceParser.NetDevice): + parentname = nodeinst.parent + try: + return VirtualHostDevice.device_from_node(conn, + name=parentname) + except: + logging.exception("Fetching net parent device failed.") + + raise ValueError(_("Node device type '%s' cannot be attached to " + " guest.") % nodeinst.device_type) + + device_from_node = staticmethod(device_from_node) + + def __init__(self, conn, nodedev): + """ + @param conn: Connection the device/guest will be installed on + @type conn: libvirt.virConnect + @param nodedev: Optional NodeDevice instance for device being + attached to the guest + @type nodedev: L{virtinst.NodeDeviceParser.NodeDevice} + """ + VirtualDevice.VirtualDevice.__init__(self, conn) + + self.mode = None + self.type = None + + self.managed = True + + self._nodedev = nodedev + + def _get_source_xml(self): + raise NotImplementedError("Must be implemented in subclass") + + def setup(self, conn = None): + """ + Perform DeviceDetach and DeviceReset calls if necessary + + @param conn: libvirt virConnect instance to use (defaults to devices + connection) + """ + raise NotImplementedError + + def get_xml_config(self): + xml = (" <hostdev mode='%s' type='%s' managed='%s'>\n" % \ + (self.mode, self.type, self.managed and "yes" or "no")) + xml += " <source>\n" + xml += self._get_source_xml() + xml += " </source>\n" + xml += " </hostdev>\n" + return xml + + +class VirtualHostDeviceUSB(VirtualHostDevice): + + def __init__(self, conn, nodedev=None): + VirtualHostDevice.__init__(self, conn, nodedev) + + self.mode = "subsystem" + self.type = "usb" + + self.vendor = None + self.product = None + + self.bus = None + self.device = None + + self._set_from_nodedev(self._nodedev) + + + def _set_from_nodedev(self, nodedev): + if not nodedev: + return + + if not isinstance(nodedev, NodeDeviceParser.USBDevice): + raise ValueError(_("'nodedev' must be a USBDevice instance.")) + + self.vendor = nodedev.vendor_id + self.product = nodedev.product_id + self.bus = nodedev.bus + self.device = nodedev.device + + def _get_source_xml(self): + xml = "" + if self.vendor and self.product: + xml += " <vendor id='%s'/>\n" % self.vendor + xml += " <product id='%s'/>\n" % self.product + elif self.bus and self.device: + xml += " <address bus='%s' device='%s'/>\n" % (self.bus, + self.device) + else: + raise RuntimeError(_("'vendor' and 'product', or 'bus' and " + " 'device' are required.")) + return xml + + + def setup(self, conn = None): + if not conn: + conn = self.conn + + # No libvirt api support for USB Detach/Reset yet + return + +class VirtualHostDevicePCI(VirtualHostDevice): + + def __init__(self, conn, nodedev=None): + VirtualHostDevice.__init__(self, conn, nodedev) + + self.mode = "subsystem" + self.type = "pci" + + self.domain = "0x0" + self.bus = None + self.slot = None + self.function = None + + self._set_from_nodedev(self._nodedev) + + + def _set_from_nodedev(self, nodedev): + if not nodedev: + return + + if not isinstance(nodedev, NodeDeviceParser.PCIDevice): + raise ValueError(_("'nodedev' must be a PCIDevice instance.")) + + self.domain = nodedev.domain + self.bus = nodedev.bus + self.slot = nodedev.slot + self.function = nodedev.function + + def _get_source_xml(self): + if not (self.domain and self.bus and self.slot and self.function): + raise RuntimeError(_("'domain', 'bus', 'slot', and 'function' " + "must be specified.")) + + xml = " <address domain='%s' bus='%s' slot='%s' function='%s'/>\n" + return xml % (self.domain, self.bus, self.slot, self.function) + + def setup(self, conn = None): + """ + Perform DeviceDetach and DeviceReset calls if necessary + + @param conn: libvirt virConnect instance to use (defaults to devices + connection) + """ + if not conn: + conn = self.conn + + if not NodeDeviceParser.is_pci_detach_capable(conn): + return + + try: + # Do this as a sanity check, so that we don't fail at domain + # start time + self._nodedev.deviceDetach() + self._nodedev.deviceReset() + except Exception, e: + raise RuntimeError(_("Could not detach PCI device: %s" % str(e))) + + diff -r daf6b88c0448 -r 17e1c491f429 virtinst/__init__.py --- a/virtinst/__init__.py Mon Mar 02 10:10:18 2009 -0500 +++ b/virtinst/__init__.py Mon Mar 02 10:40:55 2009 -0500 @@ -32,6 +32,8 @@ from VirtualGraphics import VirtualGraphics from VirtualAudio import VirtualAudio from VirtualDisk import VirtualDisk, XenDisk +from VirtualHostDevice import (VirtualHostDevice, VirtualHostDeviceUSB, + VirtualHostDevicePCI) from FullVirtGuest import FullVirtGuest from ParaVirtGuest import ParaVirtGuest from DistroInstaller import DistroInstaller
_______________________________________________ et-mgmt-tools mailing list et-mgmt-tools@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/et-mgmt-tools