[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [et-mgmt-tools] [PATCH 2/3]: Add VirtualHostDevice classes



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 redhat com>
# 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 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 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

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]