- 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(a)linux.vnet.ibm.com>
Signed-off-by: Lucio Correia <luciojhc(a)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