On 07/11/2016 04:31 PM, Jose Ricardo Ziviani wrote:
- kimchi will only finish the device attachment/detachment after
receiving the appropriate event from libvirt.
Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.com>
---
model/libvirtevents.py | 48 ++++++++++++++++++++++++++++++++++++++++
model/vmhostdevs.py | 60 +++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 98 insertions(+), 10 deletions(-)
diff --git a/model/libvirtevents.py b/model/libvirtevents.py
index dfd22c6..0f3bfd5 100644
--- a/model/libvirtevents.py
+++ b/model/libvirtevents.py
@@ -89,3 +89,51 @@ class LibvirtEvents(object):
else:
reason = e.message
wok_log.error("Register of ENOSPC event failed: %s" % reason)
+
+ def registerAttachDevicesEvent(self, conn, cb, arg):
+ """
+ register libvirt event to listen to devices attachment
+ """
+ try:
+ return conn.get().domainEventRegisterAny(
+ None,
+ libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_ADDED,
+ cb,
+ arg)
+
+ except libvirt.libvirtError as e:
+ wok_log.error("register attach event failed: %s" % e.message)
+
+ def registerDetachDevicesEvent(self, conn, cb, arg):
+ """
+ register libvirt event to listen to devices detachment
+ """
+ try:
+ return conn.get().domainEventRegisterAny(
+ None,
+ libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED,
+ cb,
+ arg)
+
+ except libvirt.libvirtError as e:
+ wok_log.error("register detach event failed: %s" % e.message)
+
+ def deregisterAttachDevicesEvent(self, conn, id):
+ """
+ deregister libvirt event to listen to devices attachment
+ """
+ try:
+ conn.get().domainEventDeregisterAny(id)
+
+ except libvirt.libvirtError as e:
+ wok_log.error("deregister attach event failed: %s" % e.message)
+
+ def deregisterDetachDevicesEvent(self, conn, id):
+ """
+ deregister libvirt event to listen to devices detachment
+ """
+ try:
+ conn.get().domainEventDeregisterAny(id)
+
+ except libvirt.libvirtError as e:
+ wok_log.error("deregister detach event failed: %s" % e.message)
These 2 functions do the same thing but with a different string at
wok_log. I think
you can do a generic detach events function that can be reused in other
cases:
def deregisterEvent(self, conn, id):
"""
deregister libvirt event based on event id
"""
+ try:
+ conn.get().domainEventDeregisterAny(id)
+
+ except libvirt.libvirtError as e:
+ wok_log.error("deregister event failed: %s" % e.message)
The information provided in the e.message string should be enough to
differentiate
whether the failure was on a detach device, attach device or any other
kind of
libvirt event.
diff --git a/model/vmhostdevs.py b/model/vmhostdevs.py
index 2130ac4..676e40a 100644
--- a/model/vmhostdevs.py
+++ b/model/vmhostdevs.py
@@ -47,8 +47,10 @@ class VMHostDevsModel(object):
def __init__(self, **kargs):
self.conn = kargs['conn']
self.objstore = kargs['objstore']
+ self.events = kargs['eventsloop']
self.caps = CapabilitiesModel(**kargs)
self.task = TaskModel(**kargs)
+ self._eid = None
def get_list(self, vmid):
dom = VMModel.get_vm(vmid, self.conn)
@@ -67,6 +69,17 @@ class VMHostDevsModel(object):
if dev_name not in eligible_dev_names:
raise InvalidParameter('KCHVMHDEV0002E', {'dev_name':
dev_name})
+ def _event_devices(self, conn, dom, alias, opaque):
+ """
+ Callback to handle add/remove devices event
+ """
+ if self._eid is None:
+ wok_log.error("callbackID cannot be None")
+ return
+
+ wok_log.info("Device %s added successfuly" % alias)
+ opaque('OK', True)
+
def create(self, vmid, params):
dev_name = params['name']
self._passthrough_device_validate(dev_name)
@@ -224,6 +237,12 @@ class VMHostDevsModel(object):
raise InvalidOperation('KCHVMHDEV0006E',
{'name': dev_info['name']})
+ if self._eid is None:
+ self._eid = self.events.registerAttachDevicesEvent(
+ self.conn,
+ self._event_devices,
+ cb)
+
This code is repeated 3 times in methods of the same class. I propose to
create
something like:
--- a/model/vmhostdevs.py
+++ b/model/vmhostdevs.py
@@ -200,6 +200,13 @@ class VMHostDevsModel(object):
return free + 1
+ def register_attach_devices_event(self, cb):
+ if self._eid is None:
+ self._eid = self.events.registerAttachDevicesEvent(
+ self.conn,
+ self._event_devices,
+ cb)
+
And then just call
register_attach_devices_event(cb)
wherever you need. Note: don't need to use this exact method 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:
@@ -273,7 +292,6 @@ class VMHostDevsModel(object):
rollback.prependDefer(dom.detachDeviceFlags, xmlstr,
device_flags)
rollback.commitAll()
- cb('OK', True)
return
for pci_info in pci_infos:
@@ -294,8 +312,6 @@ class VMHostDevsModel(object):
xmlstr, device_flags)
rollback.commitAll()
- cb('OK', True)
-
def _count_3D_devices_attached(self, dom):
counter = 0
root = objectify.fromstring(dom.XMLDesc(0))
@@ -407,6 +423,12 @@ class VMHostDevsModel(object):
dev_info = params['dev_info']
dom = VMModel.get_vm(vmid, self.conn)
+ if self._eid is None:
+ self._eid = self.events.registerAttachDevicesEvent(
+ self.conn,
+ self._event_devices,
+ cb)
+
with RollbackContext() as rollback:
cb('Reading source device XML')
xmlstr = self._get_scsi_device_xml(dev_info)
@@ -421,8 +443,6 @@ class VMHostDevsModel(object):
rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags)
rollback.commitAll()
- cb('OK', True)
-
def _get_usb_device_xml(self, dev_info):
source = E.source(
E.vendor(id=dev_info['vendor']['id']),
@@ -440,6 +460,12 @@ class VMHostDevsModel(object):
dev_info = params['dev_info']
dom = VMModel.get_vm(vmid, self.conn)
+ if self._eid is None:
+ self._eid = self.events.registerAttachDevicesEvent(
+ self.conn,
+ self._event_devices,
+ cb)
+
with RollbackContext() as rollback:
cb('Reading source device XML')
xmlstr = self._get_usb_device_xml(dev_info)
@@ -454,14 +480,14 @@ class VMHostDevsModel(object):
rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags)
rollback.commitAll()
- cb('OK', True)
-
class VMHostDevModel(object):
def __init__(self, **kargs):
self.conn = kargs['conn']
self.objstore = kargs['objstore']
+ self.events = kargs['eventsloop']
self.task = TaskModel(**kargs)
+ self._eid = None
def lookup(self, vmid, dev_name):
dom = VMModel.get_vm(vmid, self.conn)
@@ -509,6 +535,17 @@ class VMHostDevModel(object):
task_params)
return self.task.lookup(taskid)
+ def _event_devices(self, conn, dom, alias, opaque):
+ """
+ Callback to handle add/remove devices event
+ """
+ if self._eid is None:
+ wok_log.error("callbackID cannot be None")
+ return
+
+ wok_log.info("Device %s removed successfuly" % alias)
+ opaque('OK', True)
+
def _detach_device(self, cb, params):
cb('Detaching device.')
vmid = params['vmid']
@@ -526,12 +563,17 @@ class VMHostDevModel(object):
raise InvalidOperation('KCHVMHDEV0006E',
{'name': dev_info['name']})
+ if self._eid is None:
+ self._eid = self.events.registerDetachDevicesEvent(
+ self.conn,
+ self._event_devices,
+ cb)
+
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:
@@ -552,8 +594,6 @@ class VMHostDevModel(object):
raise NotFoundError('KCHVMHDEV0001E',
{'vmid': vmid, 'dev_name': dev_name})
- cb('OK', True)
-
def _get_devices_same_addr(self, hostdev, domain, bus, slot):
devices = []
for device in hostdev: