[Kimchi-devel] [PATCH V2] [Kimchi] PCI hotplug: Check USB controller, define in template, add test in Power

Lucio Correia luciojhc at linux.vnet.ibm.com
Mon Oct 17 17:26:42 UTC 2016


- Today it is not possible to hotplug a PCI in Power Systems without an
  existing USB controller in the VM. This commit checks if there
  is such controller in the VM, displaying an error message if not.
- When creating VMs using Kimchi in a Power System, the USB xhci
  controller is defined by default to have PCI hotplug support.
- From now on all templates are created with xhci usb controller, so a
  hotplug must be performed flawlessly. If anything wrong the test case
  will fail.

Signed-off-by: Jose Ricardo Ziviani <joserz at linux.vnet.ibm.com>
Signed-off-by: Lucio Correia <luciojhc at linux.vnet.ibm.com>
---
 i18n.py             |  1 +
 model/vmhostdevs.py | 35 +++++++++++++++++++++++++++++++++++
 tests/test_model.py | 25 +++++++++++++++++++++++++
 vmtemplate.py       | 13 +++++++++++++
 xmlutils/usb.py     | 45 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 119 insertions(+)
 create mode 100644 xmlutils/usb.py

Changes in V2:
* fixed import platform
* added xmlutils/usb.py for better code organization

diff --git a/i18n.py b/i18n.py
index 814a8d9..03929e5 100644
--- a/i18n.py
+++ b/i18n.py
@@ -152,6 +152,7 @@ messages = {
     "KCHVMHDEV0005E": _('The device %(name)s is probably in use by the host. Unable to attach it to the guest.'),
     "KCHVMHDEV0006E": _('Hot-(un)plug of device %(name)s is not supported.'),
     "KCHVMHDEV0007E": _('Failed to attach %(device)s to %(vm)s'),
+    "KCHVMHDEV0008E": _('VM %(vmid)s does not have a USB controller to accept PCI hotplug.'),
 
     "KCHVMIF0001E": _("Interface %(iface)s does not exist in virtual machine %(name)s"),
     "KCHVMIF0002E": _("Network %(network)s specified for virtual machine %(name)s does not exist"),
diff --git a/model/vmhostdevs.py b/model/vmhostdevs.py
index 4039240..1f893f2 100644
--- a/model/vmhostdevs.py
+++ b/model/vmhostdevs.py
@@ -20,6 +20,7 @@
 import glob
 import libvirt
 import os
+import platform
 import threading
 from lxml import etree, objectify
 from lxml.builder import E, ElementMaker
@@ -42,6 +43,9 @@ from wok.plugins.kimchi.xmlutils.qemucmdline import QEMU_NAMESPACE
 
 
 CMDLINE_FIELD_NAME = 'spapr-pci-host-bridge.mem_win_size'
+USB_MODELS_PCI_HOTPLUG = ["piix3-uhci", "piix4-uhci", "ehci", "ich9-ehci1",
+                          "ich9-uhci1", "ich9-uhci2", "ich9-uhci3",
+                          "vt82c686b-uhci", "pci-ohci", "nec-xhci"]
 WINDOW_SIZE_BAR = 0x800000000
 
 
@@ -134,6 +138,28 @@ class VMHostDevsModel(object):
 
         return '<devices>%s</devices>' % hostdevs
 
+    def have_usb_controller(self, vmid):
+        dom = VMModel.get_vm(vmid, self.conn)
+
+        root = objectify.fromstring(dom.XMLDesc(0))
+
+        try:
+            controllers = root.devices.controller
+
+        except AttributeError:
+            return False
+
+        for controller in controllers:
+
+            if 'model' not in controller.attrib:
+                continue
+
+            if controller.attrib['type'] == 'usb' and \
+               controller.attrib['model'] in USB_MODELS_PCI_HOTPLUG:
+                return True
+
+        return False
+
     def _get_pci_device_xml(self, dev_info, slot, is_multifunction):
         if 'detach_driver' not in dev_info:
             dev_info['detach_driver'] = 'kvm'
@@ -233,6 +259,15 @@ class VMHostDevsModel(object):
             dom = VMModel.get_vm(vmid, self.conn)
             driver = 'vfio' if self.caps.kernel_vfio else 'kvm'
 
+            # 'vfio' systems requires a usb controller in order to support pci
+            # hotplug on Power.
+            if driver == 'vfio' and platform.machine().startswith('ppc') and \
+               DOM_STATE_MAP[dom.info()[0]] != "shutoff" and \
+               not self.have_usb_controller(vmid):
+                msg = WokMessage('KCHVMHDEV0008E', {'vmid': vmid})
+                cb(msg.get_text(), False)
+                raise InvalidOperation("KCHVMHDEV0008E", {'vmid': vmid})
+
             # Attach all PCI devices in the same IOMMU group
             affected_names = self.devs_model.get_list(
                 _passthrough_affected_by=dev_info['name'])
diff --git a/tests/test_model.py b/tests/test_model.py
index 082cb9d..05d7415 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -24,6 +24,7 @@ import base64
 import grp
 import lxml.etree as ET
 import os
+import platform
 import pwd
 import mock
 import re
@@ -1626,6 +1627,30 @@ class ModelTests(unittest.TestCase):
             volumes = inst.storagevolumes_get_list(args['name'])
             self.assertEquals(len(volumes), 2)
 
+    def _host_is_power():
+        return platform.machine().startswith('ppc')
+
+    @unittest.skipUnless(_host_is_power(), 'Only required for Power hosts')
+    def test_pci_hotplug_requires_usb_controller(self):
+        config.set("authentication", "method", "pam")
+        inst = model.Model(None, objstore_loc=self.tmp_store)
+        tpl_params = {'name': 'test', 'memory': 1024, 'cdrom': UBUNTU_ISO}
+        inst.templates_create(tpl_params)
+
+        with RollbackContext() as rollback:
+            vm_params = {'name': 'kimchi-vm1', 'template': '/templates/test'}
+            task1 = inst.vms_create(vm_params)
+            inst.task_wait(task1['id'])
+            rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete,
+                                  'kimchi-vm1')
+            # Start vm
+            inst.vm_start('kimchi-vm1')
+            rollback.prependDefer(utils.rollback_wrapper, inst.vm_poweroff,
+                                  'kimchi-vm1')
+            # check if create VM has USB controller
+            self.assertTrue(
+                inst.vmhostdevs_have_usb_controller('kimchi-vm1'))
+
 
 class BaseModelTests(unittest.TestCase):
     class FoosModel(object):
diff --git a/vmtemplate.py b/vmtemplate.py
index 06ee845..c3390fe 100644
--- a/vmtemplate.py
+++ b/vmtemplate.py
@@ -18,6 +18,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
 import os
+import platform
 import stat
 import time
 import urlparse
@@ -40,6 +41,7 @@ from wok.plugins.kimchi.xmlutils.graphics import get_graphics_xml
 from wok.plugins.kimchi.xmlutils.interface import get_iface_xml
 from wok.plugins.kimchi.xmlutils.qemucmdline import get_qemucmdline_xml
 from wok.plugins.kimchi.xmlutils.serial import get_serial_xml
+from wok.plugins.kimchi.xmlutils.usb import get_usb_controller_xml
 
 
 class VMTemplate(object):
@@ -358,6 +360,13 @@ class VMTemplate(object):
                                         self.info['os_version'])
         return unicode(interfaces, 'utf-8')
 
+    def _get_usb_controller(self):
+        # Power systems must include USB controller model
+        if not platform.machine().startswith('ppc'):
+            return ''
+
+        return get_usb_controller_xml('nec-xhci')
+
     def _get_input_output_xml(self):
         sound = """
             <sound model='%(sound_model)s' />
@@ -469,6 +478,9 @@ class VMTemplate(object):
         # cpu_info element
         params['cpu_info_xml'] = self._get_cpu_xml()
 
+        # usb controller
+        params['usb_controller'] = self._get_usb_controller()
+
         xml = """
         <domain type='%(domain)s'>
           %(qemu-stream-cmdline)s
@@ -503,6 +515,7 @@ class VMTemplate(object):
             %(interfaces)s
             %(graphics)s
             %(input_output)s
+            %(usb_controller)s
             %(serial)s
             <memballoon model='virtio' />
           </devices>
diff --git a/xmlutils/usb.py b/xmlutils/usb.py
new file mode 100644
index 0000000..84d2aeb
--- /dev/null
+++ b/xmlutils/usb.py
@@ -0,0 +1,45 @@
+#
+# Project Kimchi
+#
+# Copyright IBM Corp, 2016
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import lxml.etree as ET
+from lxml.builder import E
+
+
+def get_usb_controller_xml(model):
+    """
+    Returns a XML string defining USB controller. Example for model='nec-xhci':
+    <controller type='usb' index='0' model='nec-xhci'>
+        <address type='pci' domain='0x0000'
+        bus='0x00' slot='0x0f' function='0x0'/>
+    </controller>
+    """
+    m = E.controller(
+        E.address(
+            type='pci',
+            domain='0x0000',
+            bus='0x00',
+            slot='0x0f',
+            function='0x0'
+        ),
+        type='usb',
+        index='0',
+        model=model
+    )
+
+    return ET.tostring(m)
-- 
2.7.4




More information about the Kimchi-devel mailing list