[PATCH V2] [Kimchi 0/4] Issue #817 - PCI attach/detach improvements.

From: Paulo Vital <pvital@linux.vnet.ibm.com> This is the backend part of the solution to Issue #817. In this solution the support for handle Libvirt events is added, as well, the attach and detach functions were modified to return AsyncTasks (then frontend can be informed when a device is really attached/detached). Patch "Add support to Libvirt Events." is the third version of previous submitted patch. Patch "Make detach device return an AsyncTask" needs submitted Wok patch "Update AsyncResource class with delete method." to work. Paulo Vital (4): Add support to Libvirt Events. Add Events support to VM's Devices attach/detach. Make attach device return an AsyncTask Make detach device return an AsyncTask control/vm/hostdevs.py | 6 +-- i18n.py | 4 ++ model/libvirtevents.py | 124 +++++++++++++++++++++++++++++++++++++++++++++ model/model.py | 6 ++- model/vmhostdevs.py | 135 ++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 254 insertions(+), 21 deletions(-) create mode 100644 model/libvirtevents.py -- 2.5.5

From: Paulo Vital <pvital@linux.vnet.ibm.com> This patch adds support to handle in Kimchi any Libvirt Event, just by adding a callback to process the event and register the event with the callback. A method called register_common_events() is responsible to register the common domain events to use a generic callback that logs into error log, the event happened and it's details. This patch is part of the solution for Kimchi Issue #817 Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- i18n.py | 4 ++ model/libvirtevents.py | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ model/model.py | 6 ++- 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 model/libvirtevents.py diff --git a/i18n.py b/i18n.py index 7cce796..8fa233f 100644 --- a/i18n.py +++ b/i18n.py @@ -324,4 +324,8 @@ messages = { "KCHLVMS0001E": _("Invalid volume group name parameter: %(name)s."), + "KCHEVENT0001E": _("Failed to register the default event implementation."), + "KCHEVENT0002E": _("Failed to register timeout event."), + "KCHEVENT0003E": _("Failed to Run the default event implementation."), + } diff --git a/model/libvirtevents.py b/model/libvirtevents.py new file mode 100644 index 0000000..86e012a --- /dev/null +++ b/model/libvirtevents.py @@ -0,0 +1,124 @@ +# +# 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 cherrypy +import libvirt +import time + +from wok.exception import OperationFailed +from wok.utils import wok_log + + +class LibvirtEvents(object): + def __init__(self): + # Register default implementation of event handlers + if libvirt.virEventRegisterDefaultImpl() < 0: + raise OperationFailed('KCHEVENT0001E') + + # Run a background thread with the event loop. Using cherrypy + # BackgroundTask class due to issues when using threading module with + # cherrypy. + self.event_loop_thread = cherrypy.process.plugins.BackgroundTask( + 2, + self._event_loop_run + ) + self.event_loop_thread.setName('KimchiLibvirtEventLoop') + self.event_loop_thread.setDaemon(True) + self.event_loop_thread.start() + + # Set an event timeout to control the self._event_loop_run + if libvirt.virEventAddTimeout(0, self._kimchi_EventTimeout, None) < 0: + raise OperationFailed('KCHEVENT0002E') + + # Event loop method to be executed in background as thread + def _event_loop_run(self): + while True: + if libvirt.virEventRunDefaultImpl() < 0: + raise OperationFailed('KCHEVENT0003E') + + def is_event_loop_alive(self): + return self.event_loop_thread.isAlive() + + # Event loop handler used to limit length of waiting for any other event. + def _kimchi_EventTimeout(self, timer, opaque): + time.sleep(1) + + def domain_event_lifecycle_cb(self, conn, dom, event, detail, *args): + """ + Callback to handle Domain (VMs) events - VM Livecycle. + """ + evStrings = ("Defined", "Undefined", "Started", "Suspended", "Resumed", + "Stopped", "Shutdown", "PMSuspended", "Crashed") + evDetails = (("Added", "Updated"), + ("Removed", ), + ("Booted", "Migrated", "Restored", "Snapshot", "Wakeup"), + ("Paused", "Migrated", "IOError", "Watchdog", "Restored", + "Snapshot", "API error"), + ("Unpaused", "Migrated", "Snapshot"), + ("Shutdown", "Destroyed", "Crashed", "Migrated", "Saved", + "Failed", "Snapshot"), + ("Finished", ), + ("Memory", "Disk"), + ("Panicked")) + msg = "Libvirt Event: Domain %s %s %s" % (dom.name(), evStrings[event], + evDetails[event][detail]) + wok_log.error(msg) + + def domain_event_reboot_cb(self, conn, dom, *args): + """ + Callback to handle Domain (VMs) events - VM Reboot. + """ + msg = "Libvirt Event: Domain %s rebooted" % dom.name() + wok_log.error(msg) + + def domain_event_pmwakeup_cb(self, conn, dom, reason, *args): + """ + Callback to handle Domain (VMs) events - VM PM WakeUp. + """ + msg = "Libvirt Event: Domain %s system pmwakeup" % dom.name() + wok_log.error(msg) + + def domain_event_pmsuspend_cb(self, conn, dom, reason, *args): + """ + Callback to handle Domain (VMs) events - VM PM Suspend. + """ + msg = "Libvirt Event: Domain %s system pmsuspend" % dom.name() + wok_log.error(msg) + + def register_common_domain_events(self, conn): + """ + Register the most common Libvirt domain events to be handled. + """ + conn = conn.get() + event_map = [(libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, + self.domain_event_lifecycle_cb), + (libvirt.VIR_DOMAIN_EVENT_ID_REBOOT, + self.domain_event_reboot_cb), + (libvirt.VIR_DOMAIN_EVENT_ID_PMWAKEUP, + self.domain_event_pmwakeup_cb), + (libvirt.VIR_DOMAIN_EVENT_ID_PMSUSPEND, + self.domain_event_pmsuspend_cb)] + + for event, event_cb in event_map: + try: + conn.domainEventRegisterAny(None, event, event_cb, None) + except libvirt.libvirtError as e: + # It's not a big deal if an event could not be registered. + wok_log.error("Could not register event %s. Details: %s" % + (event, e)) diff --git a/model/model.py b/model/model.py index e44f804..aeb8b7d 100644 --- a/model/model.py +++ b/model/model.py @@ -26,6 +26,7 @@ from wok.plugins.kimchi import config from wok.utils import import_module, listPathModules from wok.plugins.kimchi.model.libvirtconnection import LibvirtConnection +from wok.plugins.kimchi.model.libvirtevents import LibvirtEvents class Model(BaseModel): @@ -43,8 +44,11 @@ class Model(BaseModel): return instances self.objstore = ObjectStore(objstore_loc or config.get_object_store()) + self.events = LibvirtEvents() self.conn = LibvirtConnection(libvirt_uri) - kargs = {'objstore': self.objstore, 'conn': self.conn} + self.events.register_common_domain_events(self.conn) + kargs = {'objstore': self.objstore, 'conn': self.conn, + 'eventsloop': self.events} models = [] # Import task model from Wok -- 2.5.5

On 04/14/2016 06:36 PM, pvital@linux.vnet.ibm.com wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
This patch adds support to handle in Kimchi any Libvirt Event, just by adding a callback to process the event and register the event with the callback.
A method called register_common_events() is responsible to register the common domain events to use a generic callback that logs into error log, the event happened and it's details.
This patch is part of the solution for Kimchi Issue #817
Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- i18n.py | 4 ++ model/libvirtevents.py | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ model/model.py | 6 ++- 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 model/libvirtevents.py
diff --git a/i18n.py b/i18n.py index 7cce796..8fa233f 100644 --- a/i18n.py +++ b/i18n.py @@ -324,4 +324,8 @@ messages = {
"KCHLVMS0001E": _("Invalid volume group name parameter: %(name)s."),
+ "KCHEVENT0001E": _("Failed to register the default event implementation."), + "KCHEVENT0002E": _("Failed to register timeout event."), + "KCHEVENT0003E": _("Failed to Run the default event implementation."), + } diff --git a/model/libvirtevents.py b/model/libvirtevents.py new file mode 100644 index 0000000..86e012a --- /dev/null +++ b/model/libvirtevents.py @@ -0,0 +1,124 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2016
+# 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 cherrypy +import libvirt +import time + +from wok.exception import OperationFailed +from wok.utils import wok_log + + +class LibvirtEvents(object): + def __init__(self): + # Register default implementation of event handlers + if libvirt.virEventRegisterDefaultImpl() < 0: + raise OperationFailed('KCHEVENT0001E') + + # Run a background thread with the event loop. Using cherrypy + # BackgroundTask class due to issues when using threading module with + # cherrypy. + self.event_loop_thread = cherrypy.process.plugins.BackgroundTask( + 2, + self._event_loop_run + ) + self.event_loop_thread.setName('KimchiLibvirtEventLoop') + self.event_loop_thread.setDaemon(True) + self.event_loop_thread.start() + + # Set an event timeout to control the self._event_loop_run + if libvirt.virEventAddTimeout(0, self._kimchi_EventTimeout, None) < 0: + raise OperationFailed('KCHEVENT0002E') + + # Event loop method to be executed in background as thread + def _event_loop_run(self): + while True: + if libvirt.virEventRunDefaultImpl() < 0: + raise OperationFailed('KCHEVENT0003E') + + def is_event_loop_alive(self): + return self.event_loop_thread.isAlive() + + # Event loop handler used to limit length of waiting for any other event. + def _kimchi_EventTimeout(self, timer, opaque): + time.sleep(1) + + def domain_event_lifecycle_cb(self, conn, dom, event, detail, *args): + """ + Callback to handle Domain (VMs) events - VM Livecycle. + """ + evStrings = ("Defined", "Undefined", "Started", "Suspended", "Resumed", + "Stopped", "Shutdown", "PMSuspended", "Crashed") + evDetails = (("Added", "Updated"), + ("Removed", ), + ("Booted", "Migrated", "Restored", "Snapshot", "Wakeup"), + ("Paused", "Migrated", "IOError", "Watchdog", "Restored", + "Snapshot", "API error"), + ("Unpaused", "Migrated", "Snapshot"), + ("Shutdown", "Destroyed", "Crashed", "Migrated", "Saved", + "Failed", "Snapshot"), + ("Finished", ), + ("Memory", "Disk"), + ("Panicked")) + msg = "Libvirt Event: Domain %s %s %s" % (dom.name(), evStrings[event], + evDetails[event][detail]) + wok_log.error(msg) + + def domain_event_reboot_cb(self, conn, dom, *args): + """ + Callback to handle Domain (VMs) events - VM Reboot. + """ + msg = "Libvirt Event: Domain %s rebooted" % dom.name() + wok_log.error(msg) + + def domain_event_pmwakeup_cb(self, conn, dom, reason, *args): + """ + Callback to handle Domain (VMs) events - VM PM WakeUp. + """ + msg = "Libvirt Event: Domain %s system pmwakeup" % dom.name() + wok_log.error(msg) + + def domain_event_pmsuspend_cb(self, conn, dom, reason, *args): + """ + Callback to handle Domain (VMs) events - VM PM Suspend. + """ + msg = "Libvirt Event: Domain %s system pmsuspend" % dom.name() + wok_log.error(msg) + + def register_common_domain_events(self, conn): + """ + Register the most common Libvirt domain events to be handled. + """ + conn = conn.get() + event_map = [(libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, + self.domain_event_lifecycle_cb), + (libvirt.VIR_DOMAIN_EVENT_ID_REBOOT, + self.domain_event_reboot_cb), + (libvirt.VIR_DOMAIN_EVENT_ID_PMWAKEUP, + self.domain_event_pmwakeup_cb), + (libvirt.VIR_DOMAIN_EVENT_ID_PMSUSPEND, + self.domain_event_pmsuspend_cb)] + + for event, event_cb in event_map: + try: + conn.domainEventRegisterAny(None, event, event_cb, None) + except libvirt.libvirtError as e: + # It's not a big deal if an event could not be registered. + wok_log.error("Could not register event %s. Details: %s" % + (event, e)) diff --git a/model/model.py b/model/model.py index e44f804..aeb8b7d 100644 --- a/model/model.py +++ b/model/model.py @@ -26,6 +26,7 @@ from wok.plugins.kimchi import config from wok.utils import import_module, listPathModules
from wok.plugins.kimchi.model.libvirtconnection import LibvirtConnection +from wok.plugins.kimchi.model.libvirtevents import LibvirtEvents
class Model(BaseModel): @@ -43,8 +44,11 @@ class Model(BaseModel): return instances
self.objstore = ObjectStore(objstore_loc or config.get_object_store()) + self.events = LibvirtEvents() self.conn = LibvirtConnection(libvirt_uri) - kargs = {'objstore': self.objstore, 'conn': self.conn} + self.events.register_common_domain_events(self.conn) + kargs = {'objstore': self.objstore, 'conn': self.conn, + 'eventsloop': self.events} models = []
# Import task model from Wok

On Apr 15 07:01PM, Daniel Henrique Barboza wrote:
On 04/14/2016 06:36 PM, pvital@linux.vnet.ibm.com wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
This patch adds support to handle in Kimchi any Libvirt Event, just by adding a callback to process the event and register the event with the callback.
A method called register_common_events() is responsible to register the common domain events to use a generic callback that logs into error log, the event happened and it's details.
This patch is part of the solution for Kimchi Issue #817
Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- i18n.py | 4 ++ model/libvirtevents.py | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ model/model.py | 6 ++- 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 model/libvirtevents.py
diff --git a/i18n.py b/i18n.py index 7cce796..8fa233f 100644 --- a/i18n.py +++ b/i18n.py @@ -324,4 +324,8 @@ messages = { "KCHLVMS0001E": _("Invalid volume group name parameter: %(name)s."), + "KCHEVENT0001E": _("Failed to register the default event implementation."), + "KCHEVENT0002E": _("Failed to register timeout event."), + "KCHEVENT0003E": _("Failed to Run the default event implementation."), + } diff --git a/model/libvirtevents.py b/model/libvirtevents.py new file mode 100644 index 0000000..86e012a --- /dev/null +++ b/model/libvirtevents.py @@ -0,0 +1,124 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2016
+# Copyright IBM Corp, 2016
Why 'make check-local' did not pointed this for me?
+# +# 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 cherrypy +import libvirt +import time + +from wok.exception import OperationFailed +from wok.utils import wok_log + + +class LibvirtEvents(object): + def __init__(self): + # Register default implementation of event handlers + if libvirt.virEventRegisterDefaultImpl() < 0: + raise OperationFailed('KCHEVENT0001E') + + # Run a background thread with the event loop. Using cherrypy + # BackgroundTask class due to issues when using threading module with + # cherrypy. + self.event_loop_thread = cherrypy.process.plugins.BackgroundTask( + 2, + self._event_loop_run + ) + self.event_loop_thread.setName('KimchiLibvirtEventLoop') + self.event_loop_thread.setDaemon(True) + self.event_loop_thread.start() + + # Set an event timeout to control the self._event_loop_run + if libvirt.virEventAddTimeout(0, self._kimchi_EventTimeout, None) < 0: + raise OperationFailed('KCHEVENT0002E') + + # Event loop method to be executed in background as thread + def _event_loop_run(self): + while True: + if libvirt.virEventRunDefaultImpl() < 0: + raise OperationFailed('KCHEVENT0003E') + + def is_event_loop_alive(self): + return self.event_loop_thread.isAlive() + + # Event loop handler used to limit length of waiting for any other event. + def _kimchi_EventTimeout(self, timer, opaque): + time.sleep(1) + + def domain_event_lifecycle_cb(self, conn, dom, event, detail, *args): + """ + Callback to handle Domain (VMs) events - VM Livecycle. + """ + evStrings = ("Defined", "Undefined", "Started", "Suspended", "Resumed", + "Stopped", "Shutdown", "PMSuspended", "Crashed") + evDetails = (("Added", "Updated"), + ("Removed", ), + ("Booted", "Migrated", "Restored", "Snapshot", "Wakeup"), + ("Paused", "Migrated", "IOError", "Watchdog", "Restored", + "Snapshot", "API error"), + ("Unpaused", "Migrated", "Snapshot"), + ("Shutdown", "Destroyed", "Crashed", "Migrated", "Saved", + "Failed", "Snapshot"), + ("Finished", ), + ("Memory", "Disk"), + ("Panicked")) + msg = "Libvirt Event: Domain %s %s %s" % (dom.name(), evStrings[event], + evDetails[event][detail]) + wok_log.error(msg) + + def domain_event_reboot_cb(self, conn, dom, *args): + """ + Callback to handle Domain (VMs) events - VM Reboot. + """ + msg = "Libvirt Event: Domain %s rebooted" % dom.name() + wok_log.error(msg) + + def domain_event_pmwakeup_cb(self, conn, dom, reason, *args): + """ + Callback to handle Domain (VMs) events - VM PM WakeUp. + """ + msg = "Libvirt Event: Domain %s system pmwakeup" % dom.name() + wok_log.error(msg) + + def domain_event_pmsuspend_cb(self, conn, dom, reason, *args): + """ + Callback to handle Domain (VMs) events - VM PM Suspend. + """ + msg = "Libvirt Event: Domain %s system pmsuspend" % dom.name() + wok_log.error(msg) + + def register_common_domain_events(self, conn): + """ + Register the most common Libvirt domain events to be handled. + """ + conn = conn.get() + event_map = [(libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, + self.domain_event_lifecycle_cb), + (libvirt.VIR_DOMAIN_EVENT_ID_REBOOT, + self.domain_event_reboot_cb), + (libvirt.VIR_DOMAIN_EVENT_ID_PMWAKEUP, + self.domain_event_pmwakeup_cb), + (libvirt.VIR_DOMAIN_EVENT_ID_PMSUSPEND, + self.domain_event_pmsuspend_cb)] + + for event, event_cb in event_map: + try: + conn.domainEventRegisterAny(None, event, event_cb, None) + except libvirt.libvirtError as e: + # It's not a big deal if an event could not be registered. + wok_log.error("Could not register event %s. Details: %s" % + (event, e)) diff --git a/model/model.py b/model/model.py index e44f804..aeb8b7d 100644 --- a/model/model.py +++ b/model/model.py @@ -26,6 +26,7 @@ from wok.plugins.kimchi import config from wok.utils import import_module, listPathModules from wok.plugins.kimchi.model.libvirtconnection import LibvirtConnection +from wok.plugins.kimchi.model.libvirtevents import LibvirtEvents class Model(BaseModel): @@ -43,8 +44,11 @@ class Model(BaseModel): return instances self.objstore = ObjectStore(objstore_loc or config.get_object_store()) + self.events = LibvirtEvents() self.conn = LibvirtConnection(libvirt_uri) - kargs = {'objstore': self.objstore, 'conn': self.conn} + self.events.register_common_domain_events(self.conn) + kargs = {'objstore': self.objstore, 'conn': self.conn, + 'eventsloop': self.events} models = [] # Import task model from Wok
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
-- Paulo Ricardo Paz Vital Linux Technology Center, IBM Systems http://www.ibm.com/linux/ltc/

On 04/18/2016 08:38 AM, Paulo Ricardo Paz Vital wrote:
Why 'make check-local' did not pointed this for me?
[danielhb@arthas kimchi]$ sudo make check-local (..) contrib/check_i18n.py ./i18n.py Checking for invalid i18n string... Checking for invalid i18n string successfully /bin/pep8 --version 1.6.2 /bin/pep8 --filename '*.py,*.py.in' --exclude="*config.py,*i18n.py,*tests/test_config.py" . ./check_ui_code_errors.sh UI errors codes are correct Whitespace verification ... Ok IBM copyright year verification ... [danielhb@arthas kimchi]$ git diff diff --git a/model/libvirtevents.py b/model/libvirtevents.py index 86e012a..fdc269c 100644 --- a/model/libvirtevents.py +++ b/model/libvirtevents.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM, Corp. 2016 +# 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 [danielhb@arthas kimchi]$ The script will point if there is a header missing in a file, but not if an existing header was fixed.

On Apr 18 08:59AM, Daniel Henrique Barboza wrote:
On 04/18/2016 08:38 AM, Paulo Ricardo Paz Vital wrote:
Why 'make check-local' did not pointed this for me?
[danielhb@arthas kimchi]$ sudo make check-local (..) contrib/check_i18n.py ./i18n.py Checking for invalid i18n string... Checking for invalid i18n string successfully /bin/pep8 --version 1.6.2 /bin/pep8 --filename '*.py,*.py.in' --exclude="*config.py,*i18n.py,*tests/test_config.py" . ./check_ui_code_errors.sh UI errors codes are correct Whitespace verification ... Ok IBM copyright year verification ... [danielhb@arthas kimchi]$ git diff diff --git a/model/libvirtevents.py b/model/libvirtevents.py index 86e012a..fdc269c 100644 --- a/model/libvirtevents.py +++ b/model/libvirtevents.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM, Corp. 2016 +# 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 [danielhb@arthas kimchi]$
The script will point if there is a header missing in a file, but not if an existing header was fixed.
That's weird. My 'make check-local' said that the line was OK. I sent a V3 with this fix.
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
-- Paulo Ricardo Paz Vital Linux Technology Center, IBM Systems http://www.ibm.com/linux/ltc/

From: Paulo Vital <pvital@linux.vnet.ibm.com> Register libvirt events to handle and add callbacks to log information about device's add and remove into VMs. This patch is part of the solution for Kimchi Issue #817 Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- model/vmhostdevs.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/model/vmhostdevs.py b/model/vmhostdevs.py index 5f33d10..9d1d702 100644 --- a/model/vmhostdevs.py +++ b/model/vmhostdevs.py @@ -45,7 +45,9 @@ WINDOW_SIZE_BAR = 0x800000000 class VMHostDevsModel(object): def __init__(self, **kargs): self.conn = kargs['conn'] + self.events = kargs['eventsloop'] self.caps = CapabilitiesModel(**kargs) + self._register_device_event() def get_list(self, vmid): dom = VMModel.get_vm(vmid, self.conn) @@ -371,6 +373,36 @@ class VMHostDevsModel(object): dom.attachDeviceFlags(xmlstr, get_vm_config_flag(dom, mode='all')) return dev_info['name'] + def _vm_event_device_added_cb(self, conn, dom, dev, opaque): + """ + Callback to register event when device is added to domain + """ + msg = "Device %s added to domain %s" % (dev, dom.name()) + wok_log.error(msg) + + def _vm_event_device_removed_cb(self, conn, dom, dev, opaque): + """ + Callback to register event when device is removed from a domain + """ + msg = "Device %s removed from domain %s" % (dev, dom.name()) + wok_log.error(msg) + + def _register_device_event(self): + """ + Register Libvirt domain events related to add/remove devices. + """ + conn = self.conn.get() + dev_events = {'add': [libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_ADDED, + self._vm_event_device_added_cb], + 'remove': [libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED, + self._vm_event_device_removed_cb]} + try: + for event in dev_events.keys(): + conn.domainEventRegisterAny(None, dev_events[event][0], + dev_events[event][1], None) + except Exception, e: + wok_log.error("Error registering domain event: ", e) + class VMHostDevModel(object): def __init__(self, **kargs): -- 2.5.5

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 9d1d702..0cdf032 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) + device_flags = get_vm_config_flag(dom, mode='all') + 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', + 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) + device_flags = get_vm_config_flag(dom, mode='all') + 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', + 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

From: Paulo Vital <pvital@linux.vnet.ibm.com> Modified VMHostDev class to be an AsyncResource and return an AsyncTask when deleting a specific resource (a.k.a. detach a device from 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 | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/control/vm/hostdevs.py b/control/vm/hostdevs.py index b0c4d1d..e9fd5dc 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 AsyncCollection, Resource +from wok.control.base import AsyncCollection, AsyncResource from wok.control.utils import UrlSubNode @@ -46,7 +46,7 @@ class VMHostDevs(AsyncCollection): }) -class VMHostDev(Resource): +class VMHostDev(AsyncResource): def __init__(self, model, vmid, ident): super(VMHostDev, self).__init__(model, ident) self.vmid = vmid diff --git a/model/vmhostdevs.py b/model/vmhostdevs.py index 0cdf032..9057894 100644 --- a/model/vmhostdevs.py +++ b/model/vmhostdevs.py @@ -452,6 +452,8 @@ class VMHostDevsModel(object): class VMHostDevModel(object): def __init__(self, **kargs): self.conn = kargs['conn'] + self.objstore = kargs['objstore'] + self.task = TaskModel(**kargs) def lookup(self, vmid, dev_name): dom = VMModel.get_vm(vmid, self.conn) @@ -489,6 +491,23 @@ class VMHostDevModel(object): raise NotFoundError('KCHVMHDEV0001E', {'vmid': vmid, 'dev_name': dev_name}) + task_params = {'vmid': vmid, + 'dev_name': dev_name, + 'dom': dom, + 'hostdev': hostdev} + task_uri = u'/plugins/kimchi/vms/%s/hostdevs/%s' % \ + (VMModel.get_vm(vmid, self.conn).name(), dev_name) + taskid = add_task(task_uri, self._detach_device, self.objstore, + task_params) + return self.task.lookup(taskid) + + 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'] @@ -502,11 +521,14 @@ class VMHostDevModel(object): 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 @@ -514,6 +536,8 @@ class VMHostDevModel(object): raise NotFoundError('KCHVMHDEV0001E', {'vmid': vmid, 'dev_name': dev_name}) + cb('OK', True) + def _delete_affected_pci_devices(self, dom, dev_name, pci_devs): dev_model = DeviceModel(conn=self.conn) try: -- 2.5.5

Code looks good, just a minor copyright fix in patch 1. Also, I think this is the kind of work that requires some unit tests to ensure it'll endure the trials of time. On 04/14/2016 06:36 PM, pvital@linux.vnet.ibm.com wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
This is the backend part of the solution to Issue #817. In this solution the support for handle Libvirt events is added, as well, the attach and detach functions were modified to return AsyncTasks (then frontend can be informed when a device is really attached/detached).
Patch "Add support to Libvirt Events." is the third version of previous submitted patch.
Patch "Make detach device return an AsyncTask" needs submitted Wok patch "Update AsyncResource class with delete method." to work.
Paulo Vital (4): Add support to Libvirt Events. Add Events support to VM's Devices attach/detach. Make attach device return an AsyncTask Make detach device return an AsyncTask
control/vm/hostdevs.py | 6 +-- i18n.py | 4 ++ model/libvirtevents.py | 124 +++++++++++++++++++++++++++++++++++++++++++++ model/model.py | 6 ++- model/vmhostdevs.py | 135 ++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 254 insertions(+), 21 deletions(-) create mode 100644 model/libvirtevents.py
-- 2.5.5
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On Apr 15 07:05PM, Daniel Henrique Barboza wrote:
Code looks good, just a minor copyright fix in patch 1.
Also, I think this is the kind of work that requires some unit tests to ensure it'll endure the trials of time.
The tests are already handled by attach/detach test cases. Regarding Libvirt Events, since the code only log on wor_error.log file that some guest was changed and a device was added/removed, I decided to not create test case for this simple function.
On 04/14/2016 06:36 PM, pvital@linux.vnet.ibm.com wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
This is the backend part of the solution to Issue #817. In this solution the support for handle Libvirt events is added, as well, the attach and detach functions were modified to return AsyncTasks (then frontend can be informed when a device is really attached/detached).
Patch "Add support to Libvirt Events." is the third version of previous submitted patch.
Patch "Make detach device return an AsyncTask" needs submitted Wok patch "Update AsyncResource class with delete method." to work.
Paulo Vital (4): Add support to Libvirt Events. Add Events support to VM's Devices attach/detach. Make attach device return an AsyncTask Make detach device return an AsyncTask
control/vm/hostdevs.py | 6 +-- i18n.py | 4 ++ model/libvirtevents.py | 124 +++++++++++++++++++++++++++++++++++++++++++++ model/model.py | 6 ++- model/vmhostdevs.py | 135 ++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 254 insertions(+), 21 deletions(-) create mode 100644 model/libvirtevents.py
-- 2.5.5
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
-- Paulo Ricardo Paz Vital Linux Technology Center, IBM Systems http://www.ibm.com/linux/ltc/
participants (3)
-
Daniel Henrique Barboza
-
Paulo Ricardo Paz Vital
-
pvital@linux.vnet.ibm.com