def create(self, vmid, params): dev_name = params['name'] self._passthrough_device_validate(dev_name) dev_info = DeviceModel(conn=self.conn).lookup(dev_name) if dev_info['device_type'] == 'pci': taskid = add_task( u'/plugins/kimchi/vms/%s/hostdevs/' % VMModel.get_vm(vmid, self.conn).name(), self._attach_pci_device, self.objstore, { 'vmid': vmid, 'dev_info': dev_info }) return self.task.lookup(taskid) with RollbackContext() as rollback: try: dev = self.conn.get().nodeDeviceLookupByName(dev_name) dev.dettach() except Exception: raise OperationFailed('KCHVMHDEV0005E', {'name': dev_name}) else: rollback.prependDefer(dev.reAttach) rollback.commitAll() taskid = add_task( u'/plugins/kimchi/vms/%s/hostdevs/' % VMModel.get_vm(vmid, self.conn).name(), '_attach_%s_device' % dev_info['device_type'], self.objstore, { 'vmid': vmid, 'dev_info': dev_info }) return self.task.lookup(taskid)
def lookup(self, vmid, dev_name): dom = VMModel.get_vm(vmid, self.conn) xmlstr = dom.XMLDesc(0) root = objectify.fromstring(xmlstr) try: hostdev = root.devices.hostdev except AttributeError: raise NotFoundError('KCHVMHDEV0001E', { 'vmid': vmid, 'dev_name': dev_name }) dev_model = DeviceModel(conn=self.conn) for e in hostdev: deduced_name = DeviceModel.deduce_dev_name(e, self.conn) if deduced_name == dev_name: dev_info = dev_model.lookup(dev_name) return { 'name': dev_name, 'type': e.attrib['type'], 'product': dev_info.get('product', None), 'vendor': dev_info.get('vendor', None) } raise NotFoundError('KCHVMHDEV0001E', { 'vmid': vmid, 'dev_name': dev_name })
def __init__(self, **kargs): self.conn = kargs['conn'] self.objstore = kargs['objstore'] self.scanner = Scanner(self._clean_scan) self.scanner.delete() self.caps = CapabilitiesModel(**kargs) self.device = DeviceModel(**kargs) if self.conn.isQemuURI(): self._check_default_pools()
def __init__(self, **kargs): self.conn = kargs['conn'] self.objstore = kargs['objstore'] self.events = kargs['eventsloop'] self.task = TaskModel(**kargs) self.devs_model = DevicesModel(**kargs) self.dev_model = DeviceModel(**kargs) self._cb = None self.events.registerDetachDevicesEvent( self.conn, self._event_devices, self)
def _delete_affected_pci_devices(self, dom, dev_name, pci_devs): dev_model = DeviceModel(conn=self.conn) try: dev_model.lookup(dev_name) except NotFoundError: return affected_names = set( DevicesModel(conn=self.conn).get_list( _passthrough_affected_by=dev_name)) for pci_name, e in pci_devs: if pci_name in affected_names: xmlstr = etree.tostring(e) dom.detachDeviceFlags(xmlstr, get_vm_config_flag(dom, mode='all'))
def _detach_device(self, cb, params): cb('Detaching device.') vmid = params['vmid'] dev_name = params['dev_name'] dom = params['dom'] hostdev = params['hostdev'] pci_devs = [(DeviceModel.deduce_dev_name(e, self.conn), e) for e in hostdev if e.attrib['type'] == 'pci'] dev_model = DeviceModel(conn=self.conn) dev_info = dev_model.lookup(dev_name) is_3D_device = dev_model.is_device_3D_controller(dev_info) if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff": raise InvalidOperation('KCHVMHDEV0006E', {'name': dev_info['name']}) if self._hotunplug_multifunction_pci(dom, hostdev, dev_name): if is_3D_device: cb('Updating MMIO from VM...') devsmodel = VMHostDevsModel(conn=self.conn) devsmodel.update_mmio_guest(vmid, False) cb('OK', True) return for e in hostdev: if DeviceModel.deduce_dev_name(e, self.conn) == dev_name: xmlstr = etree.tostring(e) cb('Detaching device from VM...') dom.detachDeviceFlags(xmlstr, get_vm_config_flag(dom, mode='all')) if e.attrib['type'] == 'pci': cb('Deleting affected PCI devices...') self._delete_affected_pci_devices(dom, dev_name, pci_devs) if is_3D_device: cb('Updating MMIO from VM...') devsmodel = VMHostDevsModel(conn=self.conn) devsmodel.update_mmio_guest(vmid, False) break else: raise NotFoundError('KCHVMHDEV0001E', { 'vmid': vmid, 'dev_name': dev_name }) cb('OK', True)
def _count_3D_devices_attached(self, dom): counter = 0 root = objectify.fromstring(dom.XMLDesc(0)) try: hostdev = root.devices.hostdev except AttributeError: return counter for device in hostdev: if device.attrib['type'] != 'pci': continue name = DeviceModel.deduce_dev_name(device, self.conn) info = DeviceModel(conn=self.conn).lookup(name) if 'vga3d' in info and info['vga3d']: counter += 1 return counter
def delete(self, vmid, dev_name): dom = VMModel.get_vm(vmid, self.conn) xmlstr = dom.XMLDesc(0) root = objectify.fromstring(xmlstr) try: hostdev = root.devices.hostdev except AttributeError: raise NotFoundError('KCHVMHDEV0001E', { 'vmid': vmid, 'dev_name': dev_name }) pci_devs = [(DeviceModel.deduce_dev_name(e, self.conn), e) for e in hostdev if e.attrib['type'] == 'pci'] dev_model = DeviceModel(conn=self.conn) dev_info = dev_model.lookup(dev_name) is_3D_device = dev_model.is_device_3D_controller(dev_info) if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff": raise InvalidOperation('KCHVMHDEV0006E', {'name': dev_info['name']}) for e in hostdev: if DeviceModel.deduce_dev_name(e, self.conn) == dev_name: xmlstr = etree.tostring(e) dom.detachDeviceFlags(xmlstr, get_vm_config_flag(dom, mode='all')) if e.attrib['type'] == 'pci': self._delete_affected_pci_devices(dom, dev_name, pci_devs) if is_3D_device: devsmodel = VMHostDevsModel(conn=self.conn) devsmodel.update_mmio_guest(vmid, False) break else: raise NotFoundError('KCHVMHDEV0001E', { 'vmid': vmid, 'dev_name': dev_name })
def create(self, vmid, params): dev_name = params['name'] self._passthrough_device_validate(dev_name) dev_info = DeviceModel(conn=self.conn).lookup(dev_name) if dev_info['device_type'] == 'pci': return self._attach_pci_device(vmid, dev_info) with RollbackContext() as rollback: try: dev = self.conn.get().nodeDeviceLookupByName(dev_name) dev.dettach() except Exception: raise OperationFailed('KCHVMHDEV0005E', {'name': dev_name}) else: rollback.prependDefer(dev.reAttach) attach_device = getattr( self, '_attach_%s_device' % dev_info['device_type']) info = attach_device(vmid, dev_info) rollback.commitAll() return info
def _attach_pci_device(self, vmid, dev_info): self._validate_pci_passthrough_env() dom = VMModel.get_vm(vmid, self.conn) # Due to libvirt limitation, we don't support live assigne device to # vfio driver. driver = ('vfio' if DOM_STATE_MAP[dom.info()[0]] == "shutoff" and self.caps.kernel_vfio else 'kvm') # on powerkvm systems it must be vfio driver. distro, _, _ = platform.linux_distribution() if distro == 'IBM_PowerKVM': driver = 'vfio' # Attach all PCI devices in the same IOMMU group dev_model = DeviceModel(conn=self.conn) devs_model = DevicesModel(conn=self.conn) affected_names = devs_model.get_list( _passthrough_affected_by=dev_info['name']) passthrough_names = devs_model.get_list(_cap='pci', _passthrough='true') group_names = list(set(affected_names) & set(passthrough_names)) pci_infos = [dev_model.lookup(dev_name) for dev_name in group_names] pci_infos.append(dev_info) is_multifunction = len(pci_infos) > 1 and \ DOM_STATE_MAP[dom.info()[0]] == "shutoff" pci_infos = sorted(pci_infos, key=itemgetter('name')) # all devices in the group that is going to be attached to the vm # must be detached from the host first with RollbackContext() as rollback: for pci_info in pci_infos: try: dev = self.conn.get().nodeDeviceLookupByName( pci_info['name']) dev.dettach() except Exception: raise OperationFailed('KCHVMHDEV0005E', {'name': pci_info['name']}) else: rollback.prependDefer(dev.reAttach) rollback.commitAll() device_flags = get_vm_config_flag(dom, mode='all') slot = 0 if is_multifunction: slot = self._available_slot(dom) with RollbackContext() as rollback: for pci_info in pci_infos: pci_info['detach_driver'] = driver xmlstr = self._get_pci_device_xml(pci_info, slot, is_multifunction) try: dom.attachDeviceFlags(xmlstr, device_flags) except libvirt.libvirtError: wok_log.error( 'Failed to attach host device %s to VM %s: \n%s', pci_info['name'], vmid, xmlstr) raise rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags) rollback.commitAll() return dev_info['name']
def _attach_pci_device(self, cb, params): cb('Attaching PCI device') vmid = params['vmid'] dev_info = params['dev_info'] self._validate_pci_passthrough_env() dom = VMModel.get_vm(vmid, self.conn) # Due to libvirt limitation, we don't support live assigne device to # vfio driver. driver = ('vfio' if DOM_STATE_MAP[dom.info()[0]] == "shutoff" and self.caps.kernel_vfio else 'kvm') # on powerkvm systems it must be vfio driver. distro, _, _ = platform.linux_distribution() if distro == 'IBM_PowerKVM': driver = 'vfio' # Attach all PCI devices in the same IOMMU group dev_model = DeviceModel(conn=self.conn) devs_model = DevicesModel(conn=self.conn) affected_names = devs_model.get_list( _passthrough_affected_by=dev_info['name']) passthrough_names = devs_model.get_list(_cap='pci', _passthrough='true') group_names = list(set(affected_names) & set(passthrough_names)) pci_infos = [dev_model.lookup(dev_name) for dev_name in group_names] pci_infos.append(dev_info) is_multifunction = len(pci_infos) > 1 pci_infos = sorted(pci_infos, key=itemgetter('name')) # does not allow hot-plug of 3D graphic cards is_3D_device = dev_model.is_device_3D_controller(dev_info) if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff": raise InvalidOperation('KCHVMHDEV0006E', {'name': dev_info['name']}) # all devices in the group that is going to be attached to the vm # must be detached from the host first with RollbackContext() as rollback: for pci_info in pci_infos: try: dev = self.conn.get().nodeDeviceLookupByName( pci_info['name']) dev.dettach() except Exception: raise OperationFailed('KCHVMHDEV0005E', {'name': pci_info['name']}) else: rollback.prependDefer(dev.reAttach) rollback.commitAll() device_flags = get_vm_config_flag(dom, mode='all') # when attaching a 3D graphic device it might be necessary to increase # the window size memory in order to be able to attach more than one # device to the same guest if is_3D_device: self.update_mmio_guest(vmid, True) slot = 0 if is_multifunction: # search for the first available slot in guest xml slot = self._available_slot(dom) with RollbackContext() as rollback: # multifunction hotplug is a special case where all functions # must be attached together within one xml file, the same does # not happen to multifunction coldplug - where each function is # attached individually if DOM_STATE_MAP[dom.info()[0]] != 'shutoff' and is_multifunction: xmlstr = self._get_pci_devices_xml(pci_infos, slot, driver) try: dom.attachDeviceFlags(xmlstr, device_flags) except libvirt.libvirtError: wok_log.error( 'Failed to attach mutifunction device VM %s: \n%s', vmid, xmlstr) raise rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags) rollback.commitAll() cb('OK', True) return for pci_info in pci_infos: pci_info['detach_driver'] = driver cb('Reading source device XML') xmlstr = self._get_pci_device_xml(pci_info, slot, is_multifunction) try: cb('Attaching device to VM') dom.attachDeviceFlags(xmlstr, device_flags) except libvirt.libvirtError: wok_log.error( 'Failed to attach host device %s to VM %s: \n%s', pci_info['name'], vmid, xmlstr) raise rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags) rollback.commitAll() cb('OK', True)