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. Thanks, Cole
# HG changeset patch # User Cole Robinson <crobinso@xxxxxxxxxx> # Node ID 624895ec659067a0204eef702150839fa77ad01e # Parent 391354aef31a1a8e21e43ba3799af3a79c69985e Add VirtualHostDevice classes, for attaching physical host devices to a guest. diff -r 391354aef31a -r 624895ec6590 tests/nodedev-xml/devxml/pcidev.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/nodedev-xml/devxml/pcidev.xml Sun Mar 01 21:42:13 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 391354aef31a -r 624895ec6590 tests/nodedev-xml/devxml/usbdev1.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/nodedev-xml/devxml/usbdev1.xml Sun Mar 01 21:42:13 2009 -0500 @@ -0,0 +1,6 @@ + <hostdev mode='subsystem' type='usb' managed='no'> + <source> + <vendor id='0x0781'/> + <product id='0x5151'/> + </source> + </hostdev> diff -r 391354aef31a -r 624895ec6590 tests/nodedev-xml/devxml/usbdev2.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/nodedev-xml/devxml/usbdev2.xml Sun Mar 01 21:42:13 2009 -0500 @@ -0,0 +1,5 @@ + <hostdev mode='subsystem' type='usb' managed='no'> + <source> + <address bus='1' device='4'/> + </source> + </hostdev> diff -r 391354aef31a -r 624895ec6590 tests/nodedev.py --- a/tests/nodedev.py Sun Mar 01 21:29:18 2009 -0500 +++ b/tests/nodedev.py Sun Mar 01 21:42:13 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 391354aef31a -r 624895ec6590 virtinst/Guest.py --- a/virtinst/Guest.py Sun Mar 01 21:29:18 2009 -0500 +++ b/virtinst/Guest.py Sun Mar 01 21:42:13 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): @@ -576,12 +584,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 391354aef31a -r 624895ec6590 virtinst/VirtualHostDevice.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/virtinst/VirtualHostDevice.py Sun Mar 01 21:42:13 2009 -0500 @@ -0,0 +1,187 @@ +# +# 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 = False + + 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) + """ + if not conn: + conn = self.conn + + # FIXME: Check that conn supports HostDeviceDetach fu + # FIXME: How does this relate to 'managed' + #self._nodedev.deviceAttach() + #self._nodedev.deviceReset() + + 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.managed = False + + 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 + +class VirtualHostDevicePCI(VirtualHostDevice): + + def __init__(self, conn, nodedev=None): + VirtualHostDevice.__init__(self, conn, nodedev) + + self.mode = "subsystem" + self.type = "pci" + + # FIXME: Is this dependent on qemu vs. xen? + self.managed = True + + 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) diff -r 391354aef31a -r 624895ec6590 virtinst/__init__.py --- a/virtinst/__init__.py Sun Mar 01 21:29:18 2009 -0500 +++ b/virtinst/__init__.py Sun Mar 01 21:42:13 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