[PATCH v2][Wok 0/8]
by Rodrigo Trujillo
V2:
Addresses Aline's review points and other problems found:
- Added test cases for pluginsmanager;
- Fixes Sample plugin and use it in test cases;
- Start Wok server only after command line option checking;
- Keep API /plugins due to problems with authentication and
Wok initialization (see below);
- Keep API /pluginsmanager with authentication;
Authorization problem:
Tried to keep the API as pluginsmanager only, with authentication,
but got problem in UI login, due to the way Wok initializes (it
requests the plugins, them tries to load the screen, once failing
it call the login screen)
With authentication, if user is not logged, plugins request fails
and the login is not showed.
So, I kept /plugins API without authentication to show only enabled
plugins and let Wok initializes flawlessly.
Then created the /pluginsmanager, which requires authentication and
allows to enable/disable plugins.
Created this issue to improve Wok:
https://github.com/kimchi-project/wok/issues/131
Idealy, /pluginsmanager GET should be unauthenticated (to list
plugins) but POST actions should require authentication, for
security
V1:
This is initial basic version of Plugins Manager.
Features:
- Improve plugins load and initialization;
- New API to enable/disable a given plugin;
- Update plugin configuration file;
- Fix UI in order make Wok work as usual;
ToDos (improvements):
- Update (enable/disable) plugin in cherrypy (live);
- Support to plugins dependencies;
- Add command-line options to enable-disable given plugins
Rodrigo Trujillo (8):
Creates pluginmanager.py module
Changes server behavior to use pluginsmanager
Implements control/model of new plugin API
Change plugins API documentation by pluginsmanager API
Load Wok Server after options checkings
Fix Sample plugin
Fix test utils imports and add auth tests
Add Plugins Manager test cases
docs/API/plugins.md | 13 --
docs/API/pluginsmanager.md | 61 ++++++
src/wok/auth.py | 15 +-
src/wok/control/plugins.py | 31 ++-
src/wok/i18n.py | 3 +
src/wok/model/plugins.py | 38 +++-
src/wok/plugins/sample/__init__.py | 54 ++++-
src/wok/plugins/sample/control/__init__.py | 26 +++
src/wok/plugins/sample/sample.conf.in | 22 --
src/wok/plugins/sample/ui/config/tab-ext.xml | 9 +-
.../plugins/sample/ui/pages/sample-tab1.html.tmpl | 3 +
.../plugins/sample/ui/pages/sample-tab2.html.tmpl | 3 +
src/wok/pluginsmanager.py | 238 +++++++++++++++++++++
src/wok/root.py | 8 +-
src/wok/server.py | 57 +----
src/wok/utils.py | 52 -----
src/wokd.in | 6 +-
tests/test_plugin.py | 104 ++++++++-
tests/test_server.py | 10 +-
tests/utils.py | 7 +-
20 files changed, 582 insertions(+), 178 deletions(-)
delete mode 100644 docs/API/plugins.md
create mode 100644 docs/API/pluginsmanager.md
create mode 100644 src/wok/plugins/sample/control/__init__.py
create mode 100644 src/wok/pluginsmanager.py
--
2.1.0
8 years, 5 months
Re: [Kimchi-devel] [PATCH] [Kimchi] Pass only those fields that have been modified in Edit Guest; Match maxmem to mem when guest is not running or paused
by Socorro Stoppler
On 06/03/2016 02:24 PM, Aline Manera wrote:
>
> Hi Socorro,
>
> Only a minor comment below and a question.
>
> On 06/02/2016 07:23 PM, Socorro Stoppler wrote:
>> v2:
>> Made adjustments on how things were being done based on feedback.
>> Also included the fix for github issue#: 941 - max memory should
>> increase when mem is increased.
>>
>> v1:
>> This patch fixes bugzilla#: 141686 -after memory hot plug seeing
>> error 'KCHCPUHP0005E'
>> This patch addresses the issue of what is being passed down to the
>> backend
>> when guest is modified. The issue was passing parameters that were not
>> modified caused the backend to throw an error in the mem hotplug
>> scenario.
>> It now only passes down those parameters that have been modified
>> regarding
>> memory and cpu fields.
>>
>> Signed-off-by: Socorro Stoppler <socorro(a)linux.vnet.ibm.com>
>> ---
>> ui/js/src/kimchi.guest_edit_main.js | 159
>> ++++++++++++++++++++++++------------
>> ui/pages/guest-edit.html.tmpl | 2 +-
>> 2 files changed, 108 insertions(+), 53 deletions(-)
>>
>> diff --git a/ui/js/src/kimchi.guest_edit_main.js
>> b/ui/js/src/kimchi.guest_edit_main.js
>> index 4cee8fe..fc67d82 100644
>> --- a/ui/js/src/kimchi.guest_edit_main.js
>> +++ b/ui/js/src/kimchi.guest_edit_main.js
>> @@ -668,10 +668,8 @@ kimchi.guest_edit_main = function() {
>> guest['max-processor'] = guest.cpu_info['maxvcpus'];
>> guest['icon'] = guest['icon'] ||
>> 'plugins/kimchi/images/icon-vm.png';
>> $('#form-guest-edit-general').fillWithObject(guest);
>> -
>> $('#guest-edit-memory-textbox').val(parseInt(guest.memory.current));
>> $('#guest-edit-max-memory-textbox').val(parseInt(guest.memory.maxmemory));
>> -
>> kimchi.thisVMState = guest['state'];
>> refreshCDROMs();
>> $('#guest-edit-attach-cdrom-button').on('click',
>> function(event) {
>> @@ -706,6 +704,12 @@ kimchi.guest_edit_main = function() {
>> $('#guest-show-max-processor
>> i.fa').toggleClass('fa-plus-circle fa-minus-circle');
>> });
>>
>> + if ((kimchi.thisVMState !== "running") &&
>> (kimchi.thisVMState !== "paused")) {
>> + $("#guest-edit-memory-textbox").bind('keyup blur',
>> function(e) {
>> + $('#guest-edit-max-memory-textbox').val($(this).val());
>> + });
>> + }
>> +
>> var onAttached = function(params) {
>> refreshCDROMs();
>> };
>> @@ -736,59 +740,110 @@ kimchi.guest_edit_main = function() {
>> kimchi.retrieveVM(kimchi.selectedGuest, initContent);
>>
>> var generalSubmit = function(event) {
>> - $(saveButton).prop('disabled', true);
>> - var data = $('#form-guest-edit-general').serializeObject();
>> - if (data['memory'] !== undefined) {
>> - data['memory'] = Number(data['memory']);
>> - }
>> - if (data['max-memory'] !== undefined) {
>> - data['max-memory'] = Number(data['max-memory']);
>> - }
>> -
>> - // Test memory values before submit. Avoid requests we know
>> are going to fail
>> - if (parseInt($('#guest-edit-memory-textbox').val()) >
>> parseInt($('#guest-edit-max-memory-textbox').val())) {
>> - wok.message.error(i18n['KCHVM0002E'],
>> '#alert-modal-container');
>> - $(saveButton).prop('disabled', false);
>> - return;
>> - }
>> -
>> - // Test CPU values before submit. Avoid requests we know are
>> going to fail
>> - if (parseInt($('#guest-edit-cores-textbox').val()) >
>> parseInt($('#guest-edit-max-processor-textbox').val())) {
>> - wok.message.error(i18n['KCHVM0003E'],
>> '#alert-modal-container');
>> - $(saveButton).prop('disabled', false);
>> - return;
>> - }
>> -
>> - if (data['vcpus'] !== undefined) {
>> - var cpu = Number(data['vcpus']);
>> - var maxCpu = Number(data['max-processor']);
>> - if (data['max-processor'] !== undefined &&
>> data['max-processor'] !== "") {
>> - if (maxCpu >= cpu || maxCpu < cpu ) { // if maxCpu
>> < cpu, this will throw an error from backend
>> - data['cpu_info'] = {
>> - vcpus: cpu,
>> - maxvcpus: maxCpu
>> - };
>> + kimchi.retrieveVM(kimchi.selectedGuest, function(org) {
>> + $(saveButton).prop('disabled', true);
>> + var data = $('#form-guest-edit-general').serializeObject();
>> + data['memory'] = {current: Number(data['memory-ui']),
>> maxmemory: Number(data['max-memory'])};
>> + data['cpu_info'] = {maxvcpus:
>> Number(data['max-processor']), vcpus: Number(data['vcpus']),
>> topology: org['cpu_info']['topology']};
>> + var changedFields = {};
>> + for (var key in data) {
>> + valueFromUI = data[key];
>> + if (valueFromUI instanceof Object) {
>> + // Compare if Objects of original and data are
>> identical
>> + // Handle special case when key is memory and
>> guest is running as valueFromUI will return a null for max mem
>> + // since it is disabled; for cpu_info, when
>> guest is running, just skip it since no processing is required
>> + if (kimchi.thisVMState === 'running' ||
>> kimchi.thisVMState === 'paused') {
>
>> + if (key === 'cpu_info') {
>> + continue;
>> + }
>
> The above 'if' is not needed as there is nothing to do.
This above is needed as otherwise, cpu_info will be added to
changedFields due to topology having a value of {} (i.e. the Objects are
no longer the same).
>
>> + if (key === 'memory') {
>> + // Replace valueFromUI of max mem with
>> one from original as otherwise the value is undefined
>> + data['memory']['maxmemory'] =
>> org.memory.maxmemory;
>> + }
>> + }
>> + // NOTE: Be aware of using this comparison as
>> the order of the properties matter
>> + if (JSON.stringify(valueFromUI) !==
>> JSON.stringify(org[key])) {
>> + changedFields[key] = valueFromUI;
>> + }
>> + } else {
>> + if (org[key] !== undefined) {
>> + if (data[key] != org[key]) {
>> + changedFields[key] = valueFromUI;
>> + }
>> + }
>> }
>> - } else {
>> - data['cpu_info'] = {
>> - vcpus: cpu,
>> - maxvcpus: cpu
>> - };
>> }
>> - delete data['vcpus'];
>> - delete data['max-processor'];
>> - }
>> -
>> - memory = {'current': data['memory'], 'maxmemory':
>> data['max-memory']};
>> + var origMem = Number(org.memory.current);
>> + var origMaxMem = Number(org.memory.maxmemory);
>> + var origCpu = Number(org.cpu_info.vcpus);
>> + var origMaxCpu = Number(org.cpu_info.maxvcpus);
>> + var currentMem = data['memory-ui'];
>> + var currentMaxMem = data['max-memory'];
>> + var currentCpu = data['vcpus'];
>> + var currentMaxCpu = data['max-processor'];
>> +
>> + if ('memory' in changedFields) {
>> + if (currentMaxMem !== undefined) {
>> + currentMaxMem = Number(currentMaxMem);
>> + if (currentMaxMem === origMaxMem) {
>> + delete changedFields.memory.maxmemory;
>> + }
>> + } else {
>> + delete changedFields.memory.maxmemory;
>> + }
>> + if (currentMem !== undefined) {
>> + currentMem = Number(currentMem);
>> + if (kimchi.thisVMState === 'running' ||
>> kimchi.thisVMState === 'paused') {
>> + // Compare to original max mem since current
>> max mem is undefined in UI due to being disabled
>> + if (currentMem > origMaxMem) {
>> + wok.message.error(i18n['KCHVM0002E'], '#alert-modal-container');
>> + $(saveButton).prop('disabled', false);
>> + return;
>> + }
>> + }
>> + if (currentMem === origMem) {
>> + delete changedFields.memory.current;
>> + }
>> + } else {
>> + delete changedFields.memory.current;
>> + }
>> + }
>> + if ('cpu_info' in changedFields) {
>> + if (currentMaxCpu !== undefined) {
>> + currentMaxCpu = Number(currentMaxCpu);
>> + if (currentMaxCpu === origMaxCpu) {
>> + delete changedFields.cpu_info.maxvcpus;
>> + }
>> + } else {
>> + delete changedFields.cpu_info.maxvcpus;
>> + }
>> + if (currentCpu !== undefined) {
>> + currentCpu = Number(currentCpu);
>> + if (currentMaxCpu !== undefined && currentCpu >
>> currentMaxCpu) {
>> + wok.message.error(i18n['KCHVM0003E'],
>> '#alert-modal-container');
>> + $(saveButton).prop('disabled', false);
>> + return;
>> + }
>> + if (currentCpu === origCpu) {
>> + delete changedFields.cpu_info.vcpus;
>> + }
>> + if (currentMaxCpu === origMaxCpu) {
>> + delete changedFields.cpu_info.maxvcpus;
>> + }
>> + } else {
>> + delete changedFields.cpu_info.vcpus;
>> + }
>> + // Delete this as it is not applicable regardless
>> + delete changedFields.cpu_info.topology;
>> + }
>> + kimchi.updateVM(kimchi.selectedGuest, changedFields,
>> function() {
>> + kimchi.listVmsAuto();
>> + wok.window.close();
>> + }, function(err) {
>> + wok.message.error(err.responseJSON.reason,
>> '#alert-modal-container');
>> + $(saveButton).prop('disabled', false);
>> + });
>>
>> - data.memory = memory;
>> - delete data['max-memory'];
>> - kimchi.updateVM(kimchi.selectedGuest, data, function() {
>> - kimchi.listVmsAuto();
>> - wok.window.close();
>> - }, function(err) {
>> - wok.message.error(err.responseJSON.reason,
>> '#alert-modal-container');
>> - $(saveButton).prop('disabled', false);
>> });
>> };
>>
>> diff --git a/ui/pages/guest-edit.html.tmpl
>> b/ui/pages/guest-edit.html.tmpl
>> index 5b7155a..f18d42c 100644
>> --- a/ui/pages/guest-edit.html.tmpl
>> +++ b/ui/pages/guest-edit.html.tmpl
>> @@ -58,7 +58,7 @@
>> <div class="form-group">
>> <label
>> for="guest-edit-memory-textbox">$_("Memory (MB)")</label>
>> <div id="guest-memory">
>
>> - <input id="guest-edit-memory-textbox"
>> class="form-control" name="memory" type="number" min="1024"
>> step="1024" />
>> + <input id="guest-edit-memory-textbox"
>> class="form-control" name="memory-ui" type="number" min="1024"
>> step="1024" />
>
> Any special reason for that change?
I believe it was due to having a conflict w/data['memory'] that we
introduced in generalSubmit.
>
>> <button id="guest-show-max-memory"
>> class="btn btn-primary" type="button"><i class="fa
>> fa-plus-circle"></i> <span class="text">$_("More")</span></button>
>> </div>
>> </div>
>
8 years, 5 months
[PATCH] [Wok 2/2] Log time in ISO format
by Lucio Correia
Signed-off-by: Lucio Correia <luciojhc(a)linux.vnet.ibm.com>
---
src/wok/reqlogger.py | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/src/wok/reqlogger.py b/src/wok/reqlogger.py
index 2ec62b8..6f32c44 100644
--- a/src/wok/reqlogger.py
+++ b/src/wok/reqlogger.py
@@ -23,9 +23,9 @@ import json
import logging
import logging.handlers
import os.path
+import time
from cherrypy.process.plugins import BackgroundTask
-from datetime import datetime
from tempfile import NamedTemporaryFile
from wok.config import config, get_log_download_path
@@ -37,11 +37,12 @@ from wok.utils import ascii_dict, remove_old_files
FILTER_FIELDS = ['app', 'date', 'download', 'ip', 'req', 'user', 'time']
LOG_DOWNLOAD_URI = "/data/logs/%s"
LOG_DOWNLOAD_TIMEOUT = 6
-LOG_FORMAT = "[%(date)s %(time)s] %(req)-6s %(app)-11s %(ip)-15s %(user)s: " \
- "%(message)s\n"
+LOG_FORMAT = "[%(date)s %(time)s %(zone)s] %(req)-6s %(app)-11s %(ip)-15s " \
+ "%(user)s: %(message)s\n"
RECORD_TEMPLATE_DICT = {
'date': '',
'time': '',
+ 'zone': '',
'req': '',
'app': '',
'ip': '',
@@ -49,6 +50,9 @@ RECORD_TEMPLATE_DICT = {
'message': '',
}
SECONDS_PER_HOUR = 360
+TS_DATE_FORMAT = "%Y-%m-%d"
+TS_TIME_FORMAT = "%H:%M:%S"
+TS_ZONE_FORMAT = "%Z"
# Log handler setup
REQUEST_LOG_FILE = "wok-req.log"
@@ -180,10 +184,11 @@ class RequestRecord(object):
self.message = message
self.kwargs = kwargs
- # register timestamp
- timestamp = datetime.today()
- self.kwargs['date'] = timestamp.strftime('%Y-%m-%d')
- self.kwargs['time'] = timestamp.strftime('%H:%M:%S')
+ # register timestamp in local time
+ timestamp = time.localtime()
+ self.kwargs['date'] = time.strftime(TS_DATE_FORMAT, timestamp)
+ self.kwargs['time'] = time.strftime(TS_TIME_FORMAT, timestamp)
+ self.kwargs['zone'] = time.strftime(TS_ZONE_FORMAT, timestamp)
def __str__(self):
info = json.JSONEncoder().encode(self.kwargs)
--
1.9.1
8 years, 5 months
[PATCH] [Wok 1/2] Include user IP address in request log messages
by Lucio Correia
* Allow advanced log searches by IP address
* Add RECORD_TEMPLATE_DICT to avoid versioning errors
with older log entries, which don't have IP address.
* Remove duplicated comment line
Signed-off-by: Lucio Correia <luciojhc(a)linux.vnet.ibm.com>
---
src/wok/control/base.py | 10 ++++++----
src/wok/reqlogger.py | 17 ++++++++++++++---
src/wok/root.py | 6 ++++--
3 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/src/wok/control/base.py b/src/wok/control/base.py
index 4b459ed..419179a 100644
--- a/src/wok/control/base.py
+++ b/src/wok/control/base.py
@@ -146,7 +146,8 @@ class Resource(object):
msg,
app=get_plugin_from_request(),
req=method,
- user=cherrypy.session.get(USER_NAME, 'N/A')
+ user=cherrypy.session.get(USER_NAME, 'N/A'),
+ ip=cherrypy.request.remote.ip
).log()
if destructive is False or \
@@ -227,7 +228,8 @@ class Resource(object):
msg,
app=get_plugin_from_request(),
req=method,
- user=cherrypy.session.get(USER_NAME, 'N/A')
+ user=cherrypy.session.get(USER_NAME, 'N/A'),
+ ip=cherrypy.request.remote.ip
).log()
return result
@@ -374,7 +376,6 @@ class Collection(object):
except Exception as e:
# In case of errors when fetching a resource info, pass and
# log the error, so, other resources are returned
- # log the error, so, other resources are returned.
# Encoding error message as ident is also encoded value.
# This has to be done to avoid unicode error,
# as combination of encoded and unicode value results into
@@ -455,7 +456,8 @@ class Collection(object):
msg,
app=get_plugin_from_request(),
req=method,
- user=cherrypy.session.get(USER_NAME, 'N/A')
+ user=cherrypy.session.get(USER_NAME, 'N/A'),
+ ip=cherrypy.request.remote.ip
).log()
return result
diff --git a/src/wok/reqlogger.py b/src/wok/reqlogger.py
index bbd1bd0..2ec62b8 100644
--- a/src/wok/reqlogger.py
+++ b/src/wok/reqlogger.py
@@ -34,10 +34,20 @@ from wok.utils import ascii_dict, remove_old_files
# Log search setup
-FILTER_FIELDS = ['app', 'date', 'download', 'req', 'user', 'time']
+FILTER_FIELDS = ['app', 'date', 'download', 'ip', 'req', 'user', 'time']
LOG_DOWNLOAD_URI = "/data/logs/%s"
LOG_DOWNLOAD_TIMEOUT = 6
-LOG_FORMAT = "[%(date)s %(time)s] %(req)-6s %(app)-11s %(user)s: %(message)s\n"
+LOG_FORMAT = "[%(date)s %(time)s] %(req)-6s %(app)-11s %(ip)-15s %(user)s: " \
+ "%(message)s\n"
+RECORD_TEMPLATE_DICT = {
+ 'date': '',
+ 'time': '',
+ 'req': '',
+ 'app': '',
+ 'ip': '',
+ 'user': '',
+ 'message': '',
+}
SECONDS_PER_HOUR = 360
# Log handler setup
@@ -86,7 +96,8 @@ class RequestParser(object):
with fd:
for record in sortedList:
- asciiRecord = ascii_dict(record)
+ asciiRecord = RECORD_TEMPLATE_DICT
+ asciiRecord.update(ascii_dict(record))
fd.write(LOG_FORMAT % asciiRecord)
fd.close()
diff --git a/src/wok/root.py b/src/wok/root.py
index 29ea657..902e203 100644
--- a/src/wok/root.py
+++ b/src/wok/root.py
@@ -172,7 +172,8 @@ class WokRoot(Root):
msg,
app=get_plugin_from_request(),
req=method,
- user=cherrypy.session.get(auth.USER_NAME, 'N/A')
+ user=cherrypy.session.get(auth.USER_NAME, 'N/A'),
+ ip=cherrypy.request.remote.ip
).log()
return json.dumps(user_info)
@@ -187,7 +188,8 @@ class WokRoot(Root):
msg,
app=get_plugin_from_request(),
req=method,
- user=params['username']
+ user=params['username'],
+ ip=cherrypy.request.remote.ip
).log()
auth.logout()
--
1.9.1
8 years, 5 months
[PATCH] [Wok 0/2] User Request Log improvements
by Lucio Correia
Lucio Correia (2):
Include user IP address in request log messages
Log time in ISO format
src/wok/control/base.py | 10 ++++++----
src/wok/reqlogger.py | 32 ++++++++++++++++++++++++--------
src/wok/root.py | 6 ++++--
3 files changed, 34 insertions(+), 14 deletions(-)
--
1.9.1
8 years, 5 months
[PATCH] [Wok] Fixed Bootgrid pagination links cursors
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
This patch fixes active pagination links displaying text or default cursors. When the pagination is disabled it will show the "not-allowed" cursor and when active, it will use default link cursor (pointer).
Samuel Guimarães (1):
Fixed Bootgrid pagination links cursors
ui/css/src/wok.scss | 25 +++++++++++++++++++++++++
ui/css/wok.css | 16 ++++++++++++++++
2 files changed, 41 insertions(+)
--
1.9.3
8 years, 5 months
[PATCH] [Kimchi] Github #934: fix storage pool name in template-edit.html.tmpl
by dhbarboza82@gmail.com
From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
A curious behavior was happening in ui/pages/template-edit.html.tmpl.
As reported in Github issue #934, Kimchi was having trouble editing
templates in which the storage pool had spaces in the name.
Considering a storage pool name 'this is a storage pool', which
is a valid storage pool name in libvirt and Kimchi backend,
this is how the edit template HTML was being rendered when
editing a template with this pool:
<input class="template-storage-name" value="this" is=""
a="" storage="" pool="" type="text" style="display:none">
If no changes are made in the template storage pool, the
UI tries to update the template object with a storage pool
named "this", failing a backend check and raising a
storage pool not found error.
However, if any change is made in the storage pool select box,
the JS logic would set the input value properly and dodge
this malformed input tag.
This patch solves this issue by adding double quote around
the storage name element, so value="{storageName}" instead
of value={storageName}. This is how the input tag is
generated after this change:
<input class="template-storage-name" value="this is a storage pool"
type="text" style="display:none">
And the UI will always retrieve the right storage pool
name when editing a template, even if the name has spaces.
Signed-off-by: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
---
ui/pages/template-edit.html.tmpl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/pages/template-edit.html.tmpl b/ui/pages/template-edit.html.tmpl
index c880ef4..2d7deed 100644
--- a/ui/pages/template-edit.html.tmpl
+++ b/ui/pages/template-edit.html.tmpl
@@ -175,7 +175,7 @@
<script id="template-storage-pool-tmpl" type="text/html">
<div id="storageRow{storageIndex}" class='item'>
<span class="template-storage-cell storage-pool">
- <input class="template-storage-name" value={storageName} type="text" style="display:none" />
+ <input class="template-storage-name" value="{storageName}" type="text" style="display:none" />
<select id="selectStorageName-{storageIndex}" class="selectStorageName"></select>
</span>
<span class="template-storage-cell type">
--
2.5.5
8 years, 5 months
Re: [Kimchi-devel] [PATCH V2] [Kimchi 0/2] Fix VM name conflicts with snapshot reverts
by Aline Manera
Hi Lucio,
I was expecting to have all test passing on Fedora 23 with those patches
but I got one error while running the tests:
make[4]: Entering directory '/home/alinefm/wok/src/wok/plugins/kimchi/tests'
/bin/mkdir -p ../data/screenshots
./run_tests.sh
***** Running unit test: test_mock_storagepool... PASSED - Ran 1
test in 9.337s
***** Running unit test: test_authorization... PASSED - Ran 1 test
in 10.786s
***** Running unit test: test_networkxml... PASSED - Ran 10 tests
in 0.003s
***** Running unit test: test_storagepoolxml... PASSED - Ran 1 test
in 0.001s
***** Running unit test: test_livemigration... PASSED - Ran 11
tests in 0.001s
***** Running unit test: test_mock_storagevolume... FAILED
======================================================================
FAIL: test_storagevolume (test_mock_storagevolume.MockStorageVolumeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_mock_storagevolume.py", line 99, in test_storagevolume
_do_volume_test(self, model, host, ssl_port, pool_name)
File "test_model_storagevolume.py", line 139, in _do_volume_test
self.assertEquals(200, resp.status)
AssertionError: 200 != 404
----------------------------------------------------------------------
Ran 1 test in 7.703s
FAILED (failures=1)
***** Running unit test: test_config... PASSED - Ran 3 tests in 0.002s
***** Running unit test: test_vmtemplate... PASSED - Ran 7 tests in
0.453s
***** Running unit test: test_mock_network... PASSED - Ran 1 test
in 4.409s
***** Running unit test: test_model_storagepool... PASSED - Ran 1
test in 7.068s
***** Running unit test: test_model_network... PASSED - Ran 4 tests
in 12.094s
***** Running unit test: test_template... PASSED - Ran 5 tests in
28.958s
***** Running unit test: test_model_libvirtevents... PASSED - Ran 1
test in 40.671s
***** Running unit test: test_rest... PASSED - Ran 42 tests in 155.819s
***** Running unit test: test_osinfo... PASSED - Ran 5 tests in 0.025s
***** Running unit test: test_mockmodel... PASSED - Ran 5 tests in
20.952s
***** Running unit test: test_model... FAILED
======================================================================
ERROR: test_vm_edit (test_model.ModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_model.py", line 1083, in test_vm_edit
inst.vm_update('kimchi-vm1', params)
File "/home/alinefm/wok/src/wok/plugins/kimchi/model/vms.py", line
290, in update
vm_name, dom = self._static_vm_update(name, dom, params)
File "/home/alinefm/wok/src/wok/plugins/kimchi/model/vms.py", line
816, in _static_vm_update
self._redefine_snapshots(dom, snapshots_info)
File "/home/alinefm/wok/src/wok/plugins/kimchi/model/vms.py", line
737, in _redefine_snapshots
xml = xml_item_update(xml, XPATH_SNAP_VM_NAME, dom.name(), None)
File "/home/alinefm/wok/src/wok/xmlutils/utils.py", line 52, in
xml_item_update
item.text = value
File "lxml.etree.pyx", line 951, in lxml.etree._Element.text.__set__
(src/lxml/lxml.etree.c:46377)
File "apihelpers.pxi", line 695, in lxml.etree._setNodeText
(src/lxml/lxml.etree.c:20989)
File "apihelpers.pxi", line 683, in lxml.etree._createTextNode
(src/lxml/lxml.etree.c:20865)
File "apihelpers.pxi", line 1393, in lxml.etree._utf8
(src/lxml/lxml.etree.c:27155)
ValueError: All strings must be XML compatible: Unicode or ASCII, no
NULL bytes or control characters
======================================================================
ERROR: test_vm_lifecycle (test_model.ModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_model.py", line 223, in test_vm_lifecycle
inst.vm_update('kimchi-vm', {'name': u'kimchi-vm-new'})
File "/home/alinefm/wok/src/wok/plugins/kimchi/model/vms.py", line
290, in update
vm_name, dom = self._static_vm_update(name, dom, params)
File "/home/alinefm/wok/src/wok/plugins/kimchi/model/vms.py", line
823, in _static_vm_update
'err': e.get_error_message()})
OperationFailed: KCHVM0008E: KCHVM0008E
----------------------------------------------------------------------
Ran 25 tests in 94.421s
FAILED (errors=2, skipped=1)
***** Running unit test: test_host... PASSED - Ran 3 tests in 4.786s
***** Running unit test: test_model_storagevolume... FAILED
======================================================================
FAIL: test_storagevolume_action
(test_model_storagevolume.StorageVolumeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_model_storagevolume.py", line 274, in
test_storagevolume_action
_do_volume_test(self, model, host, ssl_port, 'default')
File "test_model_storagevolume.py", line 139, in _do_volume_test
self.assertEquals(200, resp.status)
AssertionError: 200 != 404
----------------------------------------------------------------------
Ran 2 tests in 10.781s
FAILED (failures=1)
[03/Jun/2016:18:03:32] ENGINE Waiting for child threads to terminate...
======================================================================
===================== Kimchi Unit Tests Summary ======================
Ran 129 tests in 408.270 seconds.
test_mock_storagevolume FAILED: (failures=1) - full log available at
/tmp/tmp.Qe5CD4LmgQ
test_model FAILED: (errors=2, skipped=1) - full log available at
/tmp/tmp.Qe5CD4LmgQ
test_model_storagevolume FAILED: (failures=1) - full log available at
/tmp/tmp.Qe5CD4LmgQ
And one VM named 'пeω-∨м' was left n my system after running the tests.
[alinefm@alinefm-TP440 kimchi]$ sudo virsh list --all
[sudo] password for alinefm:
Id Name State
----------------------------------------------------
- fedora23 shut off
- opensuse42.1 shut off
- rhel7.2 shut off
- ubuntu15.10 shut off
- windows7 shut off
- пeω-∨м shut off
Could you take a look on it?
Thanks,
Aline Manera
On 06/01/2016 03:48 PM, Lucio Correia wrote:
> Changes in V2:
> * Fix tests
>
> Lucio Correia (2):
> Always update snapshot XML with new name and UUID
> Update tests to reflect new behavior
>
> model/vms.py | 12 +++++++++++-
> tests/test_model.py | 9 ++++++---
> 2 files changed, 17 insertions(+), 4 deletions(-)
>
8 years, 5 months
[PATCHv2] [Kimchi] Properly display network interfaces when more than one exists
by Socorro Stoppler
v2:
Fix for showing all of the interface(s) when creating a network.
v1:
Display all of the interfaces when more than one exists when listing networks.
Tooltip is shown when hovering over the interface to show all of the interfaces.
Signed-off-by: Socorro Stoppler <socorro(a)linux.vnet.ibm.com>
---
ui/css/kimchi.css | 8 ++++++--
ui/css/src/modules/_network.scss | 9 ++++++---
ui/js/src/kimchi.network.js | 4 +++-
ui/js/src/kimchi.network_add_main.js | 2 +-
ui/pages/tabs/network.html.tmpl | 4 ++--
5 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/ui/css/kimchi.css b/ui/css/kimchi.css
index 49ea39a..72f340c 100644
--- a/ui/css/kimchi.css
+++ b/ui/css/kimchi.css
@@ -2156,12 +2156,16 @@ body.wok-gallery {
#network-root-container .wok-datagrid > .wok-datagrid-header > span.column-interface,
#network-root-container .wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > span.column-interface {
- width: 10.3896%;
+ width: 15.3896%;
+ padding-right: 40px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
#network-root-container .wok-datagrid > .wok-datagrid-header > span.column-space,
#network-root-container .wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > span.column-space {
- width: 30%;
+ width: 25%;
}
#network-root-container .wok-datagrid > .wok-datagrid-header > span.column-action,
diff --git a/ui/css/src/modules/_network.scss b/ui/css/src/modules/_network.scss
index 4627ab5..ab47c89 100644
--- a/ui/css/src/modules/_network.scss
+++ b/ui/css/src/modules/_network.scss
@@ -93,18 +93,21 @@
}
> span.column-interface {
- width: 10.3896%;
+ width: 15.3896%;
+ padding-right: 40px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
> span.column-space {
- width: 30%;
+ width: 25%;
}
> span.column-action {
width: 25.909%;
text-align: right;
}
-
}
.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > span {
diff --git a/ui/js/src/kimchi.network.js b/ui/js/src/kimchi.network.js
index 381449d..ac6bf74 100644
--- a/ui/js/src/kimchi.network.js
+++ b/ui/js/src/kimchi.network.js
@@ -35,6 +35,7 @@ kimchi.initNetwork = function() {
kimchi.initNetworkListView = function() {
$('.wok-mask').removeClass('hidden');
kimchi.listNetworks(function(data) {
+ $('[data-toggle="tooltip"]').tooltip();
for (var i = 0; i < data.length; i++) {
var network = {
name : data[i].name,
@@ -46,7 +47,8 @@ kimchi.initNetworkListView = function() {
} else {
network.type = data[i].connection;
}
- network.interface = data[i].interfaces ? data[i].interfaces[0] : null;
+ network.interface = data[i].interfaces ? data[i].interfaces : null;
+ network.interface.join();
network.addrSpace = data[i].subnet ? data[i].subnet : null;
network.persistent = data[i].persistent;
kimchi.addNetworkItem(network);
diff --git a/ui/js/src/kimchi.network_add_main.js b/ui/js/src/kimchi.network_add_main.js
index 85df203..fd45f34 100644
--- a/ui/js/src/kimchi.network_add_main.js
+++ b/ui/js/src/kimchi.network_add_main.js
@@ -44,7 +44,7 @@ kimchi.startNetworkCreation = function() {
kimchi.createNetwork(data, function(result) {
network.state = result.state === "active" ? "up" : "down";
- network.interface = result.interfaces ? result.interfaces[0] : i18n["KCHNET6001M"];
+ network.interface = result.interfaces ? result.interfaces : i18n["KCHNET6001M"];
network.addrSpace = result.subnet ? result.subnet : i18n["KCHNET6001M"];
network.persistent = result.persistent;
$('#networkGrid').dataGrid('addRow', kimchi.addNetworkItem(network));
diff --git a/ui/pages/tabs/network.html.tmpl b/ui/pages/tabs/network.html.tmpl
index 6ddabaa..7e2825d 100644
--- a/ui/pages/tabs/network.html.tmpl
+++ b/ui/pages/tabs/network.html.tmpl
@@ -85,11 +85,11 @@
</div>
<div id="modalWindow" class="modal fade network-modal" tabindex="-1" role="dialog" aria-labelledby="networkModalLabel" aria-hidden="true"> </div>
<script id="networkItem" type="text/html">
- <div id='{name}' class='wok-nw-grid-body remove-when-logged-off '>
+ <div id='{name}' class='wok-nw-grid-body remove-when-logged-off'>
<span class='column-state' val="{state}"><span class='network-state {state}'><i class="fa fa-power-off"></i><span class="wok-nw-loading-icon"></span></span></span><!--
--><span class='column-name' title="{name}" val="{name}">{name}</span><!--
--><span class='column-type' val="{type}">{type}</span><!--
- --><span class='column-interface' val="{interface}">{interface}</span><!--
+ --><span class='column-interface' data-placement="top" data-toggle="tooltip" title="{interface}" val="{interface}">{interface}</span><!--
--><span class='column-space' val="{addrSpace}">{addrSpace}</span><!--
--><span class='column-action' style="display:none">
<span class="pull-right">
--
2.5.0
8 years, 5 months
[PATCH] [Kimchi] Properly display network interfaces when more than one exists
by Socorro Stoppler
This patch fixes a bug in the listing of networks in that only one network
interface is being shown regardless of the number of interfaces that exist for
that network.
In addition to now displaying all of the interfaces, in the case
that there's too many to fit in the column, an ellipsis has been added to indicate
that there's more interfaces than what it being shown. A tooltip will be seen when
the user hovers over the interface(s) to show all of the interfaces.
Signed-off-by: Socorro Stoppler <socorro(a)linux.vnet.ibm.com>
---
ui/css/kimchi.css | 8 ++++++--
ui/css/src/modules/_network.scss | 9 ++++++---
ui/js/src/kimchi.network.js | 4 +++-
ui/pages/tabs/network.html.tmpl | 4 ++--
4 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/ui/css/kimchi.css b/ui/css/kimchi.css
index 49ea39a..72f340c 100644
--- a/ui/css/kimchi.css
+++ b/ui/css/kimchi.css
@@ -2156,12 +2156,16 @@ body.wok-gallery {
#network-root-container .wok-datagrid > .wok-datagrid-header > span.column-interface,
#network-root-container .wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > span.column-interface {
- width: 10.3896%;
+ width: 15.3896%;
+ padding-right: 40px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
#network-root-container .wok-datagrid > .wok-datagrid-header > span.column-space,
#network-root-container .wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > span.column-space {
- width: 30%;
+ width: 25%;
}
#network-root-container .wok-datagrid > .wok-datagrid-header > span.column-action,
diff --git a/ui/css/src/modules/_network.scss b/ui/css/src/modules/_network.scss
index 4627ab5..ab47c89 100644
--- a/ui/css/src/modules/_network.scss
+++ b/ui/css/src/modules/_network.scss
@@ -93,18 +93,21 @@
}
> span.column-interface {
- width: 10.3896%;
+ width: 15.3896%;
+ padding-right: 40px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
> span.column-space {
- width: 30%;
+ width: 25%;
}
> span.column-action {
width: 25.909%;
text-align: right;
}
-
}
.wok-datagrid > .wok-datagrid-body > .wok-datagrid-row > span {
diff --git a/ui/js/src/kimchi.network.js b/ui/js/src/kimchi.network.js
index 381449d..ac6bf74 100644
--- a/ui/js/src/kimchi.network.js
+++ b/ui/js/src/kimchi.network.js
@@ -35,6 +35,7 @@ kimchi.initNetwork = function() {
kimchi.initNetworkListView = function() {
$('.wok-mask').removeClass('hidden');
kimchi.listNetworks(function(data) {
+ $('[data-toggle="tooltip"]').tooltip();
for (var i = 0; i < data.length; i++) {
var network = {
name : data[i].name,
@@ -46,7 +47,8 @@ kimchi.initNetworkListView = function() {
} else {
network.type = data[i].connection;
}
- network.interface = data[i].interfaces ? data[i].interfaces[0] : null;
+ network.interface = data[i].interfaces ? data[i].interfaces : null;
+ network.interface.join();
network.addrSpace = data[i].subnet ? data[i].subnet : null;
network.persistent = data[i].persistent;
kimchi.addNetworkItem(network);
diff --git a/ui/pages/tabs/network.html.tmpl b/ui/pages/tabs/network.html.tmpl
index 6ddabaa..7e2825d 100644
--- a/ui/pages/tabs/network.html.tmpl
+++ b/ui/pages/tabs/network.html.tmpl
@@ -85,11 +85,11 @@
</div>
<div id="modalWindow" class="modal fade network-modal" tabindex="-1" role="dialog" aria-labelledby="networkModalLabel" aria-hidden="true"> </div>
<script id="networkItem" type="text/html">
- <div id='{name}' class='wok-nw-grid-body remove-when-logged-off '>
+ <div id='{name}' class='wok-nw-grid-body remove-when-logged-off'>
<span class='column-state' val="{state}"><span class='network-state {state}'><i class="fa fa-power-off"></i><span class="wok-nw-loading-icon"></span></span></span><!--
--><span class='column-name' title="{name}" val="{name}">{name}</span><!--
--><span class='column-type' val="{type}">{type}</span><!--
- --><span class='column-interface' val="{interface}">{interface}</span><!--
+ --><span class='column-interface' data-placement="top" data-toggle="tooltip" title="{interface}" val="{interface}">{interface}</span><!--
--><span class='column-space' val="{addrSpace}">{addrSpace}</span><!--
--><span class='column-action' style="display:none">
<span class="pull-right">
--
2.5.0
8 years, 5 months