[Wok][PATCH] Make tab name translatable and include ui/pages/tabs/* in POTFILES.in
by pkulkark@linux.vnet.ibm.com
From: Pooja Kulkarni <pkulkark(a)linux.vnet.ibm.com>
This patch makes the tab name
translatable and adds the path
ui/pages/tabs/* in POTFILES.in
Signed-off-by: Pooja Kulkarni <pkulkark(a)linux.vnet.ibm.com>
---
po/POTFILES.in | 1 +
ui/pages/i18n.json.tmpl | 1 +
2 files changed, 2 insertions(+)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index aae0ca9..c0f0eb0 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,3 +1,4 @@
# List of source files which contain translatable strings.
src/wok/i18n.py
ui/pages/*.tmpl
+ui/pages/tabs/*.tmpl
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index c3c5b69..240be65 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -24,6 +24,7 @@
#silent _ = t.gettext
#silent _t = t.gettext
{
+ "Settings": "$_("Settings")",
"WOKAPI6007E": "$_("Can not contact the host system. Verify the host system is up and that you have network connectivity to it. HTTP request response %1. ")",
"WOKAPI6003M": "$_("Cancel")",
--
2.1.0
8 years, 7 months
[PATCH] [Wok] Save notifications in memory instead of object store
by Lucio Correia
Notifications are temporary data structure, so save it in
memory instead of object store, allowing it to work even
when there is no disk space.
Signed-off-by: Lucio Correia <luciojhc(a)linux.vnet.ibm.com>
---
src/wok/control/notifications.py | 6 +++++
src/wok/model/notifications.py | 52 +++++++++++++++++++++++++++++-----------
src/wok/objectstore.py | 36 ----------------------------
src/wok/server.py | 4 ----
4 files changed, 44 insertions(+), 54 deletions(-)
diff --git a/src/wok/control/notifications.py b/src/wok/control/notifications.py
index 37d45f2..b57595e 100644
--- a/src/wok/control/notifications.py
+++ b/src/wok/control/notifications.py
@@ -21,6 +21,11 @@ from wok.control.base import Collection, Resource
from wok.control.utils import UrlSubNode
+NOTIFICATION_REQUESTS = {
+ 'DELETE': {'default': "UI notification deleted: %(ident)s"},
+}
+
+
@UrlSubNode('notifications', True)
class Notifications(Collection):
def __init__(self, model):
@@ -31,6 +36,7 @@ class Notifications(Collection):
class Notification(Resource):
def __init__(self, model, id):
super(Notification, self).__init__(model, id)
+ self.log_map = NOTIFICATION_REQUESTS
@property
def data(self):
diff --git a/src/wok/model/notifications.py b/src/wok/model/notifications.py
index 77184db..79bed03 100644
--- a/src/wok/model/notifications.py
+++ b/src/wok/model/notifications.py
@@ -17,40 +17,64 @@
# 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 datetime import datetime
+
from wok.exception import NotFoundError, OperationFailed
from wok.message import WokMessage
+from wok.utils import wok_log
+
+
+notificationsStore = {}
+
+
+def add_notification(code, args={}, plugin_name=None):
+ if not code:
+ wok_log.error("Unable to add notification: invalid code '%(code)s'" %
+ {'code': str(code)})
+ return
+
+ global notificationsStore
+ notification = notificationsStore.get(code)
+
+ # do not update timestamp if notification already exists
+ timestamp = datetime.now().isoformat() if notification is None else \
+ notification['timestamp']
+
+ args.update({"_plugin_name": plugin_name, "timestamp": timestamp})
+ notificationsStore[code] = args
class NotificationsModel(object):
def __init__(self, **kargs):
- self.objstore = kargs['objstore']
+ pass
def get_list(self):
- with self.objstore as session:
- return session.get_list('notification')
+ global notificationsStore
+ return notificationsStore.keys()
class NotificationModel(object):
def __init__(self, **kargs):
- self.objstore = kargs['objstore']
+ pass
def lookup(self, id):
- with self.objstore as session:
- notification = session.get('notification', str(id))
+ global notificationsStore
+ notification = notificationsStore.get(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}
+ # use WokMessage to translate the notification
+ if notification:
+ timestamp = notification['timestamp']
+ plugin = notification.pop('_plugin_name', None)
+ message = WokMessage(str(id), notification, plugin).get_text()
+ return {"code": id, "message": message, "timestamp": timestamp}
raise NotFoundError("WOKNOT0001E", {'id': str(id)})
def delete(self, id):
+ global notificationsStore
+
try:
- with self.objstore as session:
- session.delete('notification', str(id))
+ del notificationsStore[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 ff3796c..59354f3 100644
--- a/src/wok/objectstore.py
+++ b/src/wok/objectstore.py
@@ -23,8 +23,6 @@ import sqlite3
import threading
import traceback
-from datetime import datetime
-
try:
from collections import OrderedDict
except ImportError:
@@ -146,37 +144,3 @@ 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: invalid code '%(code)s'" %
- {'code': str(code)})
- return
-
- try:
- with ObjectStore() as session:
- notification = session.get('notification', code)
- except NotFoundError:
- notification = None
-
- try:
- # do not update timestamp if notification already exists
- timestamp = datetime.now().isoformat() if notification is None else \
- notification['timestamp']
- args.update({"_plugin_name": plugin_name, "timestamp": 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 a329ed4..902d4bf 100644
--- a/src/wok/server.py
+++ b/src/wok/server.py
@@ -33,7 +33,6 @@ 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
@@ -107,9 +106,6 @@ 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, 7 months
[PATCH] [Kimchi] Implement image rebase to avoid backing file dependency
by Jose Ricardo Ziviani
- Runs a rebase on the image that has created using backing file to
avoid such dependency. It's slower than using the backing file
itself but faster than clonning and more reliable than trusting in
backing files.
Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.com>
---
i18n.py | 1 +
model/templates.py | 8 ++++++++
utils.py | 16 +++++++++++++++-
3 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/i18n.py b/i18n.py
index 39f5e57..bcc8b96 100644
--- a/i18n.py
+++ b/i18n.py
@@ -292,6 +292,7 @@ messages = {
"KCHUTILS0003E": _("Unable to choose a virtual machine name"),
"KCHUTILS0006E": _("Cannot upgrade objectstore data."),
+ "KCHUTILS0007E": _("Impossible to rebase the image: %(error)s"),
"KCHVMSTOR0002E": _("Invalid storage type. Types supported: 'cdrom', 'disk'"),
"KCHVMSTOR0003E": _("The path '%(value)s' is not a valid local/remote path for the device"),
diff --git a/model/templates.py b/model/templates.py
index 131f86b..e8b9e51 100644
--- a/model/templates.py
+++ b/model/templates.py
@@ -34,6 +34,7 @@ from wok.xmlutils.utils import xpath_get_text
from wok.plugins.kimchi.config import get_kimchi_version
from wok.plugins.kimchi.kvmusertests import UserTests
from wok.plugins.kimchi.model.cpuinfo import CPUInfoModel
+from wok.plugins.kimchi.utils import image_rebase
from wok.plugins.kimchi.utils import pool_name_from_uri
from wok.plugins.kimchi.vmtemplate import VMTemplate
@@ -400,6 +401,13 @@ class LibvirtVMTemplate(VMTemplate):
pool = self._get_storage_pool(v['pool'])
# outgoing text to libvirt, encode('utf-8')
pool.createXML(v['xml'].encode('utf-8'), 0)
+
+ # if using backing image, rebase the image to remove the
+ # dependency on that backing image
+ if 'base' in v \
+ and 'path' in v['base'] \
+ and 'path' in v:
+ image_rebase(v['path'])
except libvirt.libvirtError as e:
raise OperationFailed("KCHVMSTOR0008E", {'error': e.message})
return vol_list
diff --git a/utils.py b/utils.py
index c4cd07d..9b9a4dc 100644
--- a/utils.py
+++ b/utils.py
@@ -30,12 +30,26 @@ from urlparse import urlparse
from wok.exception import InvalidParameter, OperationFailed
from wok.plugins.kimchi import config
from wok.plugins.kimchi.osinfo import get_template_default
-from wok.utils import wok_log
+from wok.utils import run_command, wok_log
from wok.xmlutils.utils import xpath_get_text
MAX_REDIRECTION_ALLOWED = 5
+def image_rebase(image_path):
+ wok_log.info('starting to rebase image %s' % image_path)
+ _, err, rc = run_command(['qemu-img',
+ 'rebase',
+ '-b',
+ '',
+ image_path])
+ if rc or err:
+ error = '%s - %s' % (str(rc), err)
+ raise OperationFailed("KCHUTILS0007E", {'error': error})
+
+ wok_log.info('image rebased successfully')
+
+
def _uri_to_name(collection, uri):
expr = '/plugins/kimchi/%s/(.*?)$' % collection
m = re.match(expr, uri)
--
2.7.4
8 years, 7 months
[PATCH] [Wok] Wok Toggle-Switch checkbox UI
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
This patch adds a toggle switch inspired by iOS and Material Design checkboxes.
This widget will be used in Ginger System Services front-end.
HTML syntax:
<input type="checkbox" id="checkboxid" checked="" class="wok-toggleswitch-checkbox">
<label for="checkboxid" class="wok-toggleswitch-label">
<span class="sr-only">Checkbox label hidden from UI</span></label>
JS is necessary to update label text once checkbox status is changed - Screen reader software reads the toggle switch as a regular checkbox! (you'll have to create your own code for this as it may vary from use-case)
Samuel Guimarães (1):
Added Wok Toggle-Switch checkbox UI
ui/css/src/modules/_wok-toggleswitch.scss | 80 +++++++++++++++++++++++++++++++
ui/css/src/modules/_wok-variables.scss | 3 ++
ui/css/src/wok.scss | 2 +
ui/css/wok.css | 77 +++++++++++++++++++++++++++++
4 files changed, 162 insertions(+)
create mode 100644 ui/css/src/modules/_wok-toggleswitch.scss
--
1.9.3
8 years, 7 months
[PATCH] [Wok] Fixed underline text-decoration with .btn-link classes
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
This patch removes an empty underline when putting the mouse over .btn-link buttons with icons.
Samuel Guimarães (1):
Fixed underline text-decoration with .btn-link classes
ui/css/bootstrap.custom.css | 6 +++---
ui/css/src/modules/_buttons.scss | 9 +++++++++
ui/css/wok.css | 8 ++++++++
3 files changed, 20 insertions(+), 3 deletions(-)
--
1.9.3
8 years, 7 months
[PATCH] [Wok] Issue #95: Accessibility
by ghidasy@linux.vnet.ibm.com
From: Gabriel Hidasy Rezende <ghidasy(a)br.ibm.com>
Treat ESC and Enter keyboard events on Advanced Search dialog,
Enter triggers a click on button-search, while esc closes the window.
Gabriel Hidasy Rezende (1):
Issue #95: Accessibility
ui/js/wok.user-log.js | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
--
2.5.5
8 years, 7 months
[PATCH] [Kimchi] Handle Libvirt host ENOSPC event
by Lucio Correia
When storage pool is out of space and guest requires more,
guest is paused and nothing is told to the user. This patch
uses Libvirt Event handling and Asynchronous Notifications
mechanism to tell web users about what happened.
Signed-off-by: Lucio Correia <luciojhc(a)linux.vnet.ibm.com>
---
i18n.py | 2 +-
model/libvirtevents.py | 24 ++++++++++++++++++++++++
model/model.py | 3 +++
3 files changed, 28 insertions(+), 1 deletion(-)
This patch depends on:
* [Kimchi] Add support to Libvirt Events
* [Wok] Save notifications in memory instead of object store
diff --git a/i18n.py b/i18n.py
index 569f84d..ddfa871 100644
--- a/i18n.py
+++ b/i18n.py
@@ -334,5 +334,5 @@ messages = {
"KCHEVENT0001E": _("Failed to register the default event implementation."),
"KCHEVENT0002E": _("Failed to register timeout event."),
"KCHEVENT0003E": _("Failed to Run the default event implementation."),
-
+ "KCHEVENT0004W": _("I/O error on guest '%(vm)s': storage pool out of space for %(devAlias)s (%(srcPath)s)."),
}
diff --git a/model/libvirtevents.py b/model/libvirtevents.py
index 710840a..00e9433 100644
--- a/model/libvirtevents.py
+++ b/model/libvirtevents.py
@@ -22,6 +22,8 @@ import libvirt
import time
from wok.exception import OperationFailed
+from wok.message import WokMessage
+from wok.model.notifications import add_notification
from wok.utils import wok_log
@@ -58,3 +60,25 @@ class LibvirtEvents(object):
# Event loop handler used to limit length of waiting for any other event.
def _kimchi_EventTimeout(self, timer, opaque):
time.sleep(1)
+
+ def event_enospc_cb(self, conn, dom, path, dev, action, reason, args):
+ if reason == "enospc":
+ info = {
+ "vm": dom.name(),
+ "srcPath": path,
+ "devAlias": dev,
+ }
+ add_notification("KCHEVENT0004W", info, '/plugins/kimchi')
+ msg = WokMessage("KCHEVENT0004W", info, '/plugins/kimchi')
+ wok_log.warning(msg.get_text())
+
+ def handleEnospc(self, conn):
+ """
+ Register Libvirt IO_ERROR_REASON event to handle host ENOSPC
+ """
+ conn.get().domainEventRegisterAny(
+ None,
+ libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON,
+ self.event_enospc_cb,
+ libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON
+ )
diff --git a/model/model.py b/model/model.py
index ed474d2..327d5a7 100644
--- a/model/model.py
+++ b/model/model.py
@@ -50,6 +50,9 @@ class Model(BaseModel):
'eventsloop': self.events}
models = []
+ # Register for Libvirt's host ENOSPC event and notify UI if it happens
+ self.events.handleEnospc(self.conn)
+
# Import task model from Wok
instances = get_instances('wok.model.tasks')
for instance in instances:
--
1.9.1
8 years, 7 months
[PATCH] [Kimchi 0/2] Fix issues while attaching new disk to VM
by Aline Manera
Aline Manera (2):
Bug fix: Display error when creating new disk to attach to guest
Bug fix: Do not allow user selects invalid volume format to create a
new volume
API.json | 6 +++---
docs/API.md | 6 +++---
i18n.py | 4 ++--
model/storagevolumes.py | 2 +-
model/vmstorages.py | 2 +-
tests/test_template.py | 3 +--
ui/js/src/kimchi.guest_storage_add.main.js | 11 +++++++----
ui/pages/i18n.json.tmpl | 1 +
ui/pages/template-edit.html.tmpl | 4 ----
9 files changed, 19 insertions(+), 20 deletions(-)
--
2.5.5
8 years, 7 months
[PATCH] [Kimchi] Bug fix: Allow creating network with XML special characters
by Aline Manera
libvirt will take care of scaping any XML special character so Kimchi
does not need to do anything related to that.
For example:
[alinefm@alinefm-TP440 kimchi]$ sudo virsh net-dumpxml '~!@#$%^&*()[]<>'
<network>
<name>~!@#$%^&*()[]<></name>
<uuid>3f7fc0c9-1c82-4f61-8055-62523a8827d3</uuid>
<bridge name='virbr3' stp='on' delay='0'/>
<mac address='52:54:00:77:d2:cd'/>
<ip address='192.168.3.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.3.129' end='192.168.3.254'/>
</dhcp>
</ip>
</network>
This patch also updated the test case to cover this scenario.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
model/networks.py | 2 --
tests/test_model_network.py | 2 +-
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/model/networks.py b/model/networks.py
index 9ae2ea8..333ea6d 100644
--- a/model/networks.py
+++ b/model/networks.py
@@ -23,7 +23,6 @@ import libvirt
import sys
import time
from libvirt import VIR_INTERFACE_XML_INACTIVE
-from xml.sax.saxutils import escape
from wok.exception import InvalidOperation, InvalidParameter
from wok.exception import MissingParameter, NotFoundError, OperationFailed
@@ -107,7 +106,6 @@ class NetworksModel(object):
self._set_network_vepa(params)
# create network XML
- params['name'] = escape(params['name'])
xml = to_network_xml(**params)
try:
diff --git a/tests/test_model_network.py b/tests/test_model_network.py
index 14d7288..89c9f7d 100644
--- a/tests/test_model_network.py
+++ b/tests/test_model_network.py
@@ -149,7 +149,7 @@ class NetworkTests(unittest.TestCase):
def test_network_lifecycle(self):
# Verify all the supported network type
networks = [{'name': u'kīмсhī-пet', 'connection': 'isolated'},
- {'name': u'nat-network', 'connection': 'nat'},
+ {'name': u'<!nat-network>&', 'connection': 'nat'},
{'name': u'subnet-network', 'connection': 'nat',
'subnet': '127.0.100.0/24'}]
--
2.5.5
8 years, 7 months
[PATCH] [Kimchi] Bug fix: Create VM based on Remote ISO Template
by Aline Manera
When trying to create a VM from a recent created remote ISO Template, an error
was raised:
Error in async_task 1
Traceback (most recent call last):
File "/home/alinefm/wok/src/wok/asynctask.py", line 72, in _run_helper
self.fn(cb, opaque)
File "/home/alinefm/wok/src/wok/plugins/kimchi/model/vms.py", line 168, in _create_task
vol_list = t.fork_vm_storage(vm_uuid)
File "/home/alinefm/wok/src/wok/plugins/kimchi/model/templates.py", line 397, in fork_vm_storage
vol_list = self.to_volume_list(vm_uuid)
File "/home/alinefm/wok/src/wok/plugins/kimchi/vmtemplate.py", line 262, in to_volume_list
base_fmt = imageinfo.probe_img_info(d['base'])['format']
File "/home/alinefm/wok/src/wok/plugins/kimchi/imageinfo.py", line 40, in probe_img_info
info['actual-size'] = info['actual-size'] >> 30
KeyError: 'actual-size'
It was because the remote ISO path was being set as CDROM and also as
base image, causing the issue when creating the VM.
Fix it.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
model/templates.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/model/templates.py b/model/templates.py
index 131f86b..5cdac00 100644
--- a/model/templates.py
+++ b/model/templates.py
@@ -68,10 +68,9 @@ class TemplatesModel(object):
if source_media['type'] == 'netboot':
params['netboot'] = True
return self.save_template(params)
- else:
- # Get path of source media if it's based on disk type.
- path = source_media.get('path', None)
+ # Get path of source media if it's based on disk type.
+ path = source_media.get('path', None)
if path is None:
raise InvalidParameter("KCHTMPL0016E")
@@ -80,9 +79,10 @@ class TemplatesModel(object):
if urlparse.urlparse(path).scheme in ["http", "https", "tftp", "ftp",
"ftps"]:
params["cdrom"] = path
+ return self.save_template(params)
- # image does not exists: raise error
- elif not os.path.exists(path):
+ # Local file (ISO/Img) does not exist: raise error
+ if not os.path.exists(path):
raise InvalidParameter("KCHTMPL0002E", {'path': path})
# create magic object to discover file type
--
2.5.5
8 years, 7 months