
From: Paulo Vital <pvital@linux.vnet.ibm.com> Modified VMHostDevs class to be an AsyncCollection and return an AsyncTask when creating a new resource (a.k.a. attach a device into a VM). This patch is part of the solution for Kimchi Issue #817 Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- control/vm/hostdevs.py | 4 +-- model/vmhostdevs.py | 79 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 19 deletions(-) diff --git a/control/vm/hostdevs.py b/control/vm/hostdevs.py index 8a82db0..b0c4d1d 100644 --- a/control/vm/hostdevs.py +++ b/control/vm/hostdevs.py @@ -17,7 +17,7 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -from wok.control.base import Collection, Resource +from wok.control.base import AsyncCollection, Resource from wok.control.utils import UrlSubNode @@ -33,7 +33,7 @@ VMHOSTDEV_REQUESTS = { @UrlSubNode("hostdevs") -class VMHostDevs(Collection): +class VMHostDevs(AsyncCollection): def __init__(self, model, vmid): super(VMHostDevs, self).__init__(model) self.resource = VMHostDev diff --git a/model/vmhostdevs.py b/model/vmhostdevs.py index c40c5c2..a3dfef3 100644 --- a/model/vmhostdevs.py +++ b/model/vmhostdevs.py @@ -27,8 +27,9 @@ from operator import itemgetter from wok.exception import InvalidOperation, InvalidParameter, NotFoundError from wok.exception import OperationFailed +from wok.model.tasks import TaskModel from wok.rollbackcontext import RollbackContext -from wok.utils import run_command, wok_log +from wok.utils import add_task, run_command, wok_log from wok.plugins.kimchi.model.config import CapabilitiesModel from wok.plugins.kimchi.model.host import DeviceModel, DevicesModel @@ -46,7 +47,9 @@ class VMHostDevsModel(object): def __init__(self, **kargs): self.conn = kargs['conn'] self.events = kargs['eventsloop'] + self.objstore = kargs['objstore'] self.caps = CapabilitiesModel(**kargs) + self.task = TaskModel(**kargs) self._register_device_event() def get_list(self, vmid): @@ -72,7 +75,11 @@ class VMHostDevsModel(object): dev_info = DeviceModel(conn=self.conn).lookup(dev_name) if dev_info['device_type'] == 'pci': - return self._attach_pci_device(vmid, dev_info) + 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: @@ -83,13 +90,14 @@ class VMHostDevsModel(object): 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 + 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 _get_pci_device_xml(self, dev_info, slot, is_multifunction): if 'detach_driver' not in dev_info: @@ -164,7 +172,10 @@ class VMHostDevsModel(object): return free+1 - def _attach_pci_device(self, vmid, dev_info): + 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) @@ -229,10 +240,12 @@ class VMHostDevsModel(object): with RollbackContext() as rollback: 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( @@ -243,7 +256,7 @@ class VMHostDevsModel(object): xmlstr, device_flags) rollback.commitAll() - return dev_info['name'] + cb('OK', True) def _count_3D_devices_attached(self, dom): counter = 0 @@ -350,11 +363,27 @@ class VMHostDevsModel(object): mode='subsystem', type='scsi', sgio='unfiltered') return etree.tostring(host_dev) - def _attach_scsi_device(self, vmid, dev_info): - xmlstr = self._get_scsi_device_xml(dev_info) + def _attach_scsi_device(self, cb, params): + cb('Attaching SCSI device...') + vmid = params['vmid'] + dev_info = params['dev_info'] dom = VMModel.get_vm(vmid, self.conn) - dom.attachDeviceFlags(xmlstr, get_vm_config_flag(dom, mode='all')) - return dev_info['name'] + + with RollbackContext() as rollback: + cb('Reading source device XML') + xmlstr = self._get_scsi_device_xml(dev_info) + try: + cb('Attaching device to VM') + dom.attachDeviceFlags(xmlstr, get_vm_config_flag(dom, + mode='all')) + except libvirt.libvirtError: + wok_log.error('Failed to attach host device %s to VM %s: \n%s', + dev_info['name'], vmid, xmlstr) + raise + rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags) + rollback.commitAll() + + cb('OK', True) def _get_usb_device_xml(self, dev_info): source = E.source( @@ -367,11 +396,27 @@ class VMHostDevsModel(object): ype='usb', managed='yes') return etree.tostring(host_dev) - def _attach_usb_device(self, vmid, dev_info): - xmlstr = self._get_usb_device_xml(dev_info) + def _attach_usb_device(self, cb, params): + cb('Attaching USB device...') + vmid = params['vmid'] + dev_info = params['dev_info'] dom = VMModel.get_vm(vmid, self.conn) - dom.attachDeviceFlags(xmlstr, get_vm_config_flag(dom, mode='all')) - return dev_info['name'] + + with RollbackContext() as rollback: + cb('Reading source device XML') + xmlstr = self._get_usb_device_xml(dev_info) + try: + cb('Attaching device to VM') + dom.attachDeviceFlags(xmlstr, get_vm_config_flag(dom, + mode='all')) + except libvirt.libvirtError: + wok_log.error('Failed to attach host device %s to VM %s: \n%s', + dev_info['name'], vmid, xmlstr) + raise + rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags) + rollback.commitAll() + + cb('OK', True) def _vm_event_device_added_cb(self, conn, dom, dev, opaque): """ -- 2.5.5