[PATCH] [Kimchi] Disable Template when it has invalid parameters
by peterpnns@gmail.com
From: peterpennings <peterpnns(a)gmail.com>
This patch adds a block behavior on the action to select invalid templates when creating a guest
peterpennings (1):
Disable Template when it has invalid parameters
ui/css/kimchi.css | 28 ++++++++++++++++++---
ui/css/src/modules/_guests.scss | 6 +++++
ui/css/src/modules/_templates.scss | 18 +++++++++++---
ui/js/src/kimchi.guest_add_main.js | 8 ++++++
ui/js/src/kimchi.network_add_main.js | 48 +++++++++++++++++++++++++-----------
ui/js/src/kimchi.template_main.js | 9 +++++--
ui/pages/guest-add.html.tmpl | 7 +++---
ui/pages/tabs/templates.html.tmpl | 6 +++--
8 files changed, 102 insertions(+), 28 deletions(-)
--
2.5.0
8 years, 6 months
[PATCH] [Wok 0/3] Implement Asynchronous Notifications frontend
by Lucio Correia
This patchset depends on Asynchronous Notification backend.
Lucio Correia (3):
Implement notifications JS API
Implement wok.notification widget
Implement notifications loop
ui/js/src/wok.api.js | 18 ++++++++++++++++++
ui/js/src/wok.main.js | 5 +++++
ui/js/src/wok.notification.js | 36 ++++++++++++++++++++++++++++++++++++
ui/js/src/wok.utils.js | 19 +++++++++++++++++++
4 files changed, 78 insertions(+)
create mode 100644 ui/js/src/wok.notification.js
--
1.9.1
8 years, 7 months
[PATCH V2] [Kimchi] Add support to Libvirt Events.
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)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(a)linux.vnet.ibm.com>
---
i18n.py | 4 +++
model/libvirtevents.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++
model/model.py | 6 +++-
3 files changed, 106 insertions(+), 1 deletion(-)
create mode 100644 model/libvirtevents.py
diff --git a/i18n.py b/i18n.py
index 6214687..3939aba 100644
--- a/i18n.py
+++ b/i18n.py
@@ -326,4 +326,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..2775476
--- /dev/null
+++ b/model/libvirtevents.py
@@ -0,0 +1,97 @@
+#
+# 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_generic_cb(self, conn, dom, event, detail, *args):
+ """
+ Generic callback to handle Domain (VMs) events.
+ """
+ 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 register_common_domain_events(self, conn):
+ """
+ Register the most common Libvirt domain events to be handled.
+ """
+ conn = conn.get()
+ for ev in (libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
+ libvirt.VIR_DOMAIN_EVENT_ID_REBOOT,
+ libvirt.VIR_DOMAIN_EVENT_ID_RTC_CHANGE,
+ libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON,
+ libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS,
+ libvirt.VIR_DOMAIN_EVENT_ID_BLOCK_JOB,
+ libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG):
+ conn.domainEventRegisterAny(None, ev, self.domain_event_generic_cb,
+ ev)
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
8 years, 7 months
[PATCH v4][Kimchi 0/4] Use a single field to create a template
by Ramon Medeiros
Instead of specify if the media is cdrom or disk, use source_media to create a template
Changes:
v2:
Remove libvirt connection from VMtemplate
Fix incorrect changes on tests
Fix return status when source_media not passed
v3:
Fix pep8 issues
Remove/add some constants
Rewrite API message
Ramon Medeiros (4):
Create a single field to pass the installation media
Fix checking duplicate template before creating it
Identify installation media while creating template
Update tests
API.json | 5 ++
i18n.py | 2 +-
model/templates.py | 15 ++++--
tests/test_authorization.py | 4 +-
tests/test_livemigration.py | 7 +--
tests/test_mockmodel.py | 13 +++---
tests/test_model.py | 94 ++++++++++++++++++++-----------------
tests/test_rest.py | 36 +++++++--------
tests/test_template.py | 47 +++++++++----------
tests/test_vmtemplate.py | 40 ++++++++--------
vmtemplate.py | 110 +++++++++++++++++++++++++++++++++++++-------
11 files changed, 235 insertions(+), 138 deletions(-)
--
2.5.5
8 years, 7 months
[PATCH V2] [Wok] Implement Asynchronous Notifications backend
by Lucio Correia
* There is no POST method: a notification is
added through add_notification() method.
* Notifications are always stored in Wok object store under
'notification' type, even if added by a plugin.
* Every time Wok is started, all notifications are erased,
since this is a UI feature, intended to be showed to the
user asynchronously, independent of which tab is opened.
Signed-off-by: Lucio Correia <luciojhc(a)linux.vnet.ibm.com>
---
docs/API/notifications.md | 34 ++++++++++++++++++++++++
src/wok/control/notifications.py | 37 ++++++++++++++++++++++++++
src/wok/i18n.py | 3 +++
src/wok/model/notifications.py | 56 ++++++++++++++++++++++++++++++++++++++++
src/wok/objectstore.py | 39 ++++++++++++++++++++++++++++
src/wok/server.py | 4 +++
6 files changed, 173 insertions(+)
create mode 100644 docs/API/notifications.md
create mode 100644 src/wok/control/notifications.py
create mode 100644 src/wok/model/notifications.py
Changes in V2:
- added timestamp
- code as required parameter in add_notification
- added DELETE api for notification
This patch depends on WokMessage V3 patch
diff --git a/docs/API/notifications.md b/docs/API/notifications.md
new file mode 100644
index 0000000..851352f
--- /dev/null
+++ b/docs/API/notifications.md
@@ -0,0 +1,34 @@
+## REST API Specification for Notifications
+
+### Collection: Notifications
+
+**URI:** /notifications
+
+**Methods:**
+
+* **GET**: Retrieve a summarized list of current Notifications
+
+#### Examples
+GET /notifications
+[{Notification1}, {Notification2}, ...]
+
+### Resource: Notification
+
+**URI:** /notifications/*:id*
+
+A task represents an asynchronous operation that is being performed by the
+server.
+
+**Methods:**
+
+* **GET**: Retrieve the full description of the Notification
+ * code: message ID
+ * message: message text already translated
+
+#### Examples
+GET /notifications/KCHLIBVIRT0001W
+{
+ id: "KCHLIBVIRT0001W",
+ message: "KCHLIBVIRT0001W: Lack of storage space in guest vm-1",
+ timestamp: first time notification was emitted
+}
diff --git a/src/wok/control/notifications.py b/src/wok/control/notifications.py
new file mode 100644
index 0000000..37d45f2
--- /dev/null
+++ b/src/wok/control/notifications.py
@@ -0,0 +1,37 @@
+#
+# Project Wok
+#
+# 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
+
+from wok.control.base import Collection, Resource
+from wok.control.utils import UrlSubNode
+
+
+@UrlSubNode('notifications', True)
+class Notifications(Collection):
+ def __init__(self, model):
+ super(Notifications, self).__init__(model)
+ self.resource = Notification
+
+
+class Notification(Resource):
+ def __init__(self, model, id):
+ super(Notification, self).__init__(model, id)
+
+ @property
+ def data(self):
+ return self.info
diff --git a/src/wok/i18n.py b/src/wok/i18n.py
index e6087f4..5a2876f 100644
--- a/src/wok/i18n.py
+++ b/src/wok/i18n.py
@@ -45,6 +45,9 @@ messages = {
"WOKLOG0001E": _("Invalid filter parameter. Filter parameters allowed: %(filters)s"),
"WOKLOG0002E": _("Creation of log file failed: %(err)s"),
+ "WOKNOT0001E": _("Unable to find notification %(id)s"),
+ "WOKNOT0002E": _("Unable to delete notification %(id)s: %(message)s"),
+
"WOKOBJST0001E": _("Unable to find %(item)s in datastore"),
"WOKUTILS0001E": _("Unable to reach %(url)s. Make sure it is accessible and try again."),
diff --git a/src/wok/model/notifications.py b/src/wok/model/notifications.py
new file mode 100644
index 0000000..77184db
--- /dev/null
+++ b/src/wok/model/notifications.py
@@ -0,0 +1,56 @@
+#
+# Project Wok
+#
+# 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
+
+from wok.exception import NotFoundError, OperationFailed
+from wok.message import WokMessage
+
+
+class NotificationsModel(object):
+ def __init__(self, **kargs):
+ self.objstore = kargs['objstore']
+
+ def get_list(self):
+ with self.objstore as session:
+ return session.get_list('notification')
+
+
+class NotificationModel(object):
+ def __init__(self, **kargs):
+ self.objstore = kargs['objstore']
+
+ def lookup(self, id):
+ with self.objstore as session:
+ notification = session.get('notification', str(id))
+
+ # use WokMessage to translate the notification
+ if notification:
+ timestamp = notification['timestamp']
+ plugin = notification.pop('_plugin_name', None)
+ message = WokMessage(id, notification, plugin).get_text()
+ return {"code": id, "message": message, "timestamp": timestamp}
+
+ raise NotFoundError("WOKNOT0001E", {'id': str(id)})
+
+ def delete(self, id):
+ try:
+ with self.objstore as session:
+ session.delete('notification', str(id))
+ except Exception as e:
+ raise OperationFailed("WOKNOT0002E", {'id': str(id),
+ 'msg': e.msg()})
diff --git a/src/wok/objectstore.py b/src/wok/objectstore.py
index 59354f3..dbc3ee3 100644
--- a/src/wok/objectstore.py
+++ b/src/wok/objectstore.py
@@ -23,6 +23,8 @@ import sqlite3
import threading
import traceback
+from datetime import datetime
+
try:
from collections import OrderedDict
except ImportError:
@@ -144,3 +146,40 @@ class ObjectStore(object):
# exception again
wok_log.error(traceback.format_exc())
return False
+
+
+def add_notification(code, args={}, plugin_name=None):
+ if not code:
+ wok_log.error("Unable to add notification: invalide code '%(code)s'" %
+ {'code': str(code)})
+ return
+
+ found = True
+ try:
+ with ObjectStore() as session:
+ notification = session.get('notification', code)
+ except NotFoundError:
+ found = False
+
+ try:
+ args.update({"_plugin_name": plugin_name,
+ "timestamp": datetime.now().isoformat()})
+
+ # do not update timestamp if notification already exists
+ if found:
+ args["timestamp"] = notification["timestamp"]
+
+ with ObjectStore() as session:
+ session.store('notification', code, args)
+ except Exception as e:
+ wok_log.error("Unable to store notification: %s" % e.message)
+
+
+def clean_notifications():
+ try:
+ with ObjectStore() as session:
+ notifications = session.get_list('notification')
+ for item in notifications:
+ session.delete('notification', item)
+ except Exception as e:
+ wok_log.error("Unable to clean notifications: %s" % e.message)
diff --git a/src/wok/server.py b/src/wok/server.py
index 902d4bf..a329ed4 100644
--- a/src/wok/server.py
+++ b/src/wok/server.py
@@ -33,6 +33,7 @@ from wok.config import config as configParser
from wok.config import paths, PluginConfig, WokConfig
from wok.control import sub_nodes
from wok.model import model
+from wok.objectstore import clean_notifications
from wok.proxy import start_proxy, terminate_proxy
from wok.reqlogger import RequestLogger
from wok.root import WokRoot
@@ -106,6 +107,9 @@ class Server(object):
if dev_env:
cherrypy.log.screen = True
+ # clean object store notifications
+ clean_notifications()
+
# close standard file handlers because we are going to use a
# watchedfiled handler, otherwise we will have two file handlers
# pointing to the same file, duplicating log enries
--
1.9.1
8 years, 8 months
[PATCH V3] [Wok] Implement WokMessage class
by Lucio Correia
WokMessage is a generic message implementation, which looks
up and translates error messages for all plugins and Wok
itself.
Lookup and translation code was adapted from WokException
class, which now just uses WokMessage.
WokMessage is intended to be used by other modules dealing
with messages, such as User Request Log and future feature
Asynchronous Notifications.
Signed-off-by: Lucio Correia <luciojhc(a)linux.vnet.ibm.com>
---
src/wok/exception.py | 45 +++--------------------------
src/wok/message.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 85 insertions(+), 41 deletions(-)
create mode 100644 src/wok/message.py
Changes in V3:
- Aline's review
diff --git a/src/wok/exception.py b/src/wok/exception.py
index 023334b..52f007e 100644
--- a/src/wok/exception.py
+++ b/src/wok/exception.py
@@ -20,53 +20,16 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import cherrypy
-import gettext
-from wok.i18n import messages as _messages
-from wok.template import get_lang, validate_language
+from wok.message import WokMessage
class WokException(Exception):
def __init__(self, code='', args={}):
self.code = code
-
- for key, value in args.iteritems():
- if isinstance(value, unicode):
- continue
-
- # value is not unicode: convert it
- try:
- # In case the value formats itself to an ascii string.
- args[key] = unicode(str(value), 'utf-8')
- except UnicodeEncodeError:
- # In case the value is a WokException or it formats
- # itself to a unicode string.
- args[key] = unicode(value)
-
- # First, check if it is a Wok error message, then search in plugin
- # error messages list
- msg = _messages.get(code, code)
- if (msg == code) and (cherrypy.request.app):
- msg = self._get_translation()
-
- msg = unicode(msg, 'utf-8') % args
- pattern = "%s: %s" % (code, msg)
- cherrypy.log.error_log.error(pattern)
- Exception.__init__(self, pattern)
-
- def _get_translation(self):
- lang = validate_language(get_lang())
- paths = cherrypy.request.app.root.paths
- domain = cherrypy.request.app.root.domain
- messages = cherrypy.request.app.root.messages
- text = messages.get(self.code, self.code)
-
- try:
- translation = gettext.translation(domain, paths.mo_dir, [lang])
- except:
- translation = gettext
-
- return translation.gettext(text)
+ msg = WokMessage(code, args).get_text()
+ cherrypy.log.error_log.error(msg)
+ Exception.__init__(self, msg)
class NotFoundError(WokException):
diff --git a/src/wok/message.py b/src/wok/message.py
new file mode 100644
index 0000000..414788c
--- /dev/null
+++ b/src/wok/message.py
@@ -0,0 +1,81 @@
+#
+# Project Wok
+#
+# Copyright IBM Corp, 2015-2016
+#
+# Code derived from Project Kimchi
+#
+# 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 gettext
+
+from wok.template import get_lang, validate_language
+
+
+class WokMessage(object):
+ def __init__(self, code='', args={}, plugin=None):
+ # make all args unicode
+ for key, value in args.iteritems():
+ if isinstance(value, unicode):
+ continue
+
+ try:
+ # In case the value formats itself to an ascii string.
+ args[key] = unicode(str(value), 'utf-8')
+ except UnicodeEncodeError:
+ # In case the value is a WokException or it formats
+ # itself to a unicode string.
+ args[key] = unicode(value)
+
+ self.code = code
+ self.args = args
+ self.plugin = plugin
+
+ def _get_translation(self):
+ wok_app = cherrypy.tree.apps['']
+
+ # get app from plugin path if specified
+ if self.plugin:
+ app = cherrypy.tree.apps[self.plugin]
+ # if on request, try to get app from it
+ elif cherrypy.request.app:
+ app = cherrypy.request.app
+ # fallback: get root app (WokRoot)
+ else:
+ app = wok_app
+
+ # fallback to Wok message in case plugins raise Wok exceptions
+ text = app.root.messages.get(self.code, None)
+ if text is None:
+ app = wok_app
+ text = app.root.messages.get(self.code, self.code)
+
+ # do translation
+ domain = app.root.domain
+ paths = app.root.paths
+ lang = validate_language(get_lang())
+
+ try:
+ translation = gettext.translation(domain, paths.mo_dir, [lang])
+ except:
+ translation = gettext
+
+ return translation.gettext(text)
+
+ def get_text(self):
+ msg = self._get_translation()
+ msg = unicode(msg, 'utf-8') % self.args
+ return "%s: %s" % (self.code, msg)
--
1.9.1
8 years, 8 months
[PATCH v3][Kimchi 0/4] Use a single field to create a template
by Ramon Medeiros
Instead of specify if the media is cdrom or disk, use source_media to create a template
Ramon Medeiros (4):
Create a single field to pass the installation media
Fix checking duplicate template before creating it
Identify installation media while creating template
Update tests
API.json | 5 ++
i18n.py | 2 +-
model/templates.py | 15 ++++--
tests/test_authorization.py | 4 +-
tests/test_livemigration.py | 7 +--
tests/test_mockmodel.py | 13 +++---
tests/test_model.py | 94 ++++++++++++++++++++-----------------
tests/test_rest.py | 36 +++++++-------
tests/test_template.py | 47 +++++++++----------
tests/test_vmtemplate.py | 40 +++++++++-------
vmtemplate.py | 111 +++++++++++++++++++++++++++++++++++++-------
11 files changed, 236 insertions(+), 138 deletions(-)
--
2.5.5
8 years, 8 months
[PATCH v2][Kimchi 0/4] Use a single field to create a template
by Ramon Medeiros
Instead of specify if the media is cdrom or disk, use source_media to create a template
Ramon Medeiros (4):
Create a single field to pass the installation media
Fix checking duplicate template before creating it
Identify installation media while creating template
Update tests
API.json | 5 ++
i18n.py | 2 +-
model/templates.py | 15 ++++--
tests/test_authorization.py | 4 +-
tests/test_livemigration.py | 7 +--
tests/test_mockmodel.py | 14 +++---
tests/test_model.py | 91 ++++++++++++++++++++---------------
tests/test_rest.py | 32 ++++++-------
tests/test_template.py | 47 ++++++++-----------
tests/test_vmtemplate.py | 38 +++++++--------
vmtemplate.py | 112 ++++++++++++++++++++++++++++++++++++++------
11 files changed, 232 insertions(+), 135 deletions(-)
--
2.5.5
8 years, 8 months
[PATCH] [Kimchi] Add support to Libvirt Events.
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)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(a)linux.vnet.ibm.com>
---
i18n.py | 4 ++
model/libvirtevents.py | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++
model/model.py | 6 ++-
3 files changed, 108 insertions(+), 1 deletion(-)
create mode 100644 model/libvirtevents.py
diff --git a/i18n.py b/i18n.py
index 6214687..3939aba 100644
--- a/i18n.py
+++ b/i18n.py
@@ -326,4 +326,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..44da18f
--- /dev/null
+++ b/model/libvirtevents.py
@@ -0,0 +1,99 @@
+#
+# 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')
+
+ # List to control which domains are using events
+ self.vms_tracked = []
+
+ # 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 event_generic_cb(self, conn, dom, event, detail, *args):
+ """
+ Generic callback to handle Domain (VMs) events.
+ """
+ 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 register_common_events(self, conn):
+ """
+ Register the most common Libvirt events to be handled.
+ """
+ conn = conn.get()
+ for ev in (libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
+ libvirt.VIR_DOMAIN_EVENT_ID_REBOOT,
+ libvirt.VIR_DOMAIN_EVENT_ID_RTC_CHANGE,
+ libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON,
+ libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS,
+ libvirt.VIR_DOMAIN_EVENT_ID_BLOCK_JOB,
+ libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG):
+ conn.domainEventRegisterAny(None, ev, self.event_generic_cb, ev)
diff --git a/model/model.py b/model/model.py
index e44f804..dd50ec2 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_events(self.conn)
+ kargs = {'objstore': self.objstore, 'conn': self.conn,
+ 'eventsloop': self.events}
models = []
# Import task model from Wok
--
2.5.0
8 years, 8 months
[PATCH v2][Kimchi] Fix memory value return when hotplug memory devs
by Rodrigo Trujillo
There is an issue when Kimchi returns the value of memory, if the guest
is running. It is returning the current memory value, instead of the
total memory, which includes the value of the memory devices added.
This patch fix this problem returning the total value: current memory
+ total of memory devs.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
model/vms.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/model/vms.py b/model/vms.py
index e3bc266..08c3e45 100644
--- a/model/vms.py
+++ b/model/vms.py
@@ -1228,7 +1228,11 @@ class VMModel(object):
unit = 'KiB'
memory = convert_data_size(val, unit, 'MiB')
else:
- memory = info[2] >> 10
+ # Return current memory plus the amount of memory given by memory
+ # devices
+ root = ET.fromstring(xml)
+ totMemDevs = len(root.findall('./devices/memory')) * 1024
+ memory = (info[2] >> 10) + totMemDevs
# assure there is no zombie process left
for proc in self._serial_procs[:]:
--
2.1.0
8 years, 8 months