[PATCH] [Kimchi 0/3] Issue #201: Handling libvirt connection failures.
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)linux.vnet.ibm.com>
This is the backend part of the solution to solve issue #201. Frontend still
needs some updates to provide more information to user.
Paulo Vital (3):
Update Kimchi config file for Systemd service
Make Cheerypy up if not able to connect to libvirt
Add support to check if libvirtd is running.
contrib/kimchid.service.fedora | 3 ++-
contrib/kimchid.service.ubuntu | 3 ++-
i18n.py | 3 +++
model/config.py | 29 +++++++++++++++++++++--------
model/libvirtconnection.py | 13 +++++++++++--
model/templates.py | 5 ++++-
utils.py | 16 +++++++++++++++-
7 files changed, 58 insertions(+), 14 deletions(-)
--
2.5.5
8 years, 7 months
[PATCH] [Wok 0/3] Introduced converters in line-chart and formatMeasurement
by pkulkark@in.ibm.com
From: Pooja Kulkarni <pkulkark(a)linux.vnet.ibm.com>
1) Added converters in line-chart widget
to format data in generic way.
2) Replaced locale formatting with converter.
3) Renamed wok.datetimeLocaleConverters to
wok.localeConverters to have all the locale
specific converters in one place.
Pooja Kulkarni (3):
Introduced converters in formatMeasurement
Introduced converters in wok.line-chart widget
Replaced wok.dateTimeLocaleConverters to wok.localeConverters
ui/js/src/wok.line-chart.js | 19 +++++++++++++++++--
ui/js/src/wok.utils.js | 33 +++++++++++++++++++++++++--------
ui/js/wok.user-log.js | 2 +-
3 files changed, 43 insertions(+), 11 deletions(-)
--
2.1.0
8 years, 7 months
[PATCH] [Wok 0/3] Introduced converters in line-chart and formatMeasurement
by pkulkark@in.ibm.com
From: Pooja Kulkarni <pkulkark(a)linux.vnet.ibm.com>
1) Added converters in line-chart widget
to format data in generic way.
2) Replaced locale formatting with converter.
3) Renamed wok.datetimeLocaleConverters to
wok.localeConverters to have all the locale
specific converters in one place.
Pooja Kulkarni (3):
Introduced converters in formatMeasurement
Introduced converters in wok.line-chart widget
Replaced wok.dateTimeLocaleConverters to wok.localeConverters
ui/js/src/wok.line-chart.js | 19 +++++++++++++++++--
ui/js/src/wok.utils.js | 33 +++++++++++++++++++++++++--------
ui/js/wok.user-log.js | 2 +-
3 files changed, 43 insertions(+), 11 deletions(-)
--
2.1.0
8 years, 7 months
[PATCH] [Wok 0/3] Introduced converters in line-chart and formatMeasurement
by pkulkark@linux.vnet.ibm.com
From: Pooja Kulkarni <pkulkark(a)linux.vnet.ibm.com>
1) Added converters in line-chart widget
to format data in generic way.
2) Replaced locale formatting with converter.
3) Renamed wok.datetimeLocaleConverters to
wok.localeConverters to have all the locale
specific converters in one place.
Pooja Kulkarni (3):
Introduced converters in formatMeasurement
Introduced converters in wok.line-chart widget
Replaced wok.dateTimeLocaleConverters to wok.localeConverters
ui/js/src/wok.line-chart.js | 19 +++++++++++++++++--
ui/js/src/wok.utils.js | 33 +++++++++++++++++++++++++--------
ui/js/wok.user-log.js | 2 +-
3 files changed, 43 insertions(+), 11 deletions(-)
--
2.1.0
8 years, 7 months
[PATCH] [Wok 0/2] fix for issue #114
by sureshab@linux.vnet.ibm.com
From: Suresh Babu Angadi <sureshab(a)in.ibm.com>
Adding help page for "Settings" tab
Suresh Babu Angadi (2):
fix for issue #114
made changes in config, to add '/help' uri
made Makefile.am and configure.ac changes to
add "help" directory
added dita-hepl.xsl, css file for help pages and
help page for "Settings" tab.
configure.ac | 2 +
src/wok/config.py.in | 6 ++
tests/test_config.py.in | 5 +
ui/pages/Makefile.am | 2 +-
ui/pages/help/Makefile.am | 37 +++++++
ui/pages/help/dita-help.xsl | 44 ++++++++
ui/pages/help/en_US/Makefile.am | 26 +++++
ui/pages/help/en_US/settings.dita | 43 ++++++++
ui/pages/help/wok.css | 208 ++++++++++++++++++++++++++++++++++++++
9 files changed, 372 insertions(+), 1 deletion(-)
create mode 100644 ui/pages/help/Makefile.am
create mode 100644 ui/pages/help/dita-help.xsl
create mode 100644 ui/pages/help/en_US/Makefile.am
create mode 100644 ui/pages/help/en_US/settings.dita
create mode 100644 ui/pages/help/wok.css
--
2.1.0
8 years, 7 months
[PATCH V2] [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 | 1 +
model/libvirtevents.py | 25 +++++++++++++++++++++++++
model/model.py | 3 +++
3 files changed, 29 insertions(+)
Change in V2:
- Rebase with latest master
diff --git a/i18n.py b/i18n.py
index 49b7f1c..c697d52 100644
--- a/i18n.py
+++ b/i18n.py
@@ -337,4 +337,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 ab5c17d..00e9433 100644
--- a/model/libvirtevents.py
+++ b/model/libvirtevents.py
@@ -22,6 +22,9 @@ 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
class LibvirtEvents(object):
@@ -57,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] 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.
None event is being registered by this patch and they need be added by developer
by demand in the code. To see how register an event, the unit test
test_libvirtevents.py register two events and then use them in the code.
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
i18n.py | 3 +
model/libvirtevents.py | 59 ++++++++++
model/model.py | 5 +-
tests/test_model_libvirtevents.py | 219 ++++++++++++++++++++++++++++++++++++++
4 files changed, 285 insertions(+), 1 deletion(-)
create mode 100644 model/libvirtevents.py
create mode 100644 tests/test_model_libvirtevents.py
diff --git a/i18n.py b/i18n.py
index 2301e60..49b7f1c 100644
--- a/i18n.py
+++ b/i18n.py
@@ -334,4 +334,7 @@ messages = {
"KCHCONN0001E": _("Unable to establish connection with libvirt. Please check your libvirt URI which is often defined in /etc/libvirt/libvirt.conf"),
"KCHCONN0002E": _("Libvirt service is not active. Please start the libvirt service in your host system."),
+ "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..ab5c17d
--- /dev/null
+++ b/model/libvirtevents.py
@@ -0,0 +1,59 @@
+#
+# 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
+
+
+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)
diff --git a/model/model.py b/model/model.py
index e44f804..ed474d2 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,10 @@ 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}
+ kargs = {'objstore': self.objstore, 'conn': self.conn,
+ 'eventsloop': self.events}
models = []
# Import task model from Wok
diff --git a/tests/test_model_libvirtevents.py b/tests/test_model_libvirtevents.py
new file mode 100644
index 0000000..d7dc675
--- /dev/null
+++ b/tests/test_model_libvirtevents.py
@@ -0,0 +1,219 @@
+# -*- coding: utf-8 -*-
+#
+# 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 json
+import libvirt
+import os
+import shutil
+import tempfile
+import time
+import unittest
+
+import tests.utils as utils
+
+from wok.basemodel import Singleton
+from wok.rollbackcontext import RollbackContext
+
+from wok.plugins.kimchi.model import model
+
+import iso_gen
+
+
+TMP_DIR = '/var/lib/kimchi/tests/'
+UBUNTU_ISO = TMP_DIR + 'ubuntu14.04.iso'
+TMP_EVENT = None
+EVENT_ID = 0
+
+
+def setUpModule():
+ global TMP_DIR, TMP_EVENT
+
+ if not os.path.exists(TMP_DIR):
+ os.makedirs(TMP_DIR)
+
+ TMP_EVENT = tempfile.mktemp()
+
+ iso_gen.construct_fake_iso(UBUNTU_ISO, True, '14.04', 'ubuntu')
+
+ # Some FeatureTests functions depend on server to validate their result.
+ # As CapabilitiesModel is a Singleton class it will get the first result
+ # from FeatureTests which may be wrong when using the Model instance
+ # directly - the case of this test_model.py
+ # So clean Singleton instances to make sure to get the right result when
+ # running the following tests.
+ Singleton._instances = {}
+
+
+def tearDownModule():
+ global TMP_DIR, TMP_EVENT
+
+ os.unlink(TMP_EVENT)
+ shutil.rmtree(TMP_DIR)
+
+
+def _get_next_event_id():
+ global EVENT_ID
+ EVENT_ID += 1
+ return EVENT_ID
+
+
+def _get_event_id():
+ global EVENT_ID
+ return EVENT_ID
+
+
+def _store_event(data):
+ global TMP_EVENT
+ with open(TMP_EVENT, "a") as file:
+ file.write("%s\n" % data)
+
+
+def _get_event(id):
+ global TMP_EVENT
+ with open(TMP_EVENT, "r") as file:
+ for event in [line.rstrip('\n') for line in file.readlines()]:
+ fields = event.split('|')
+ if fields[0] == id:
+ return fields[1]
+
+
+class LibvirtEventsTests(unittest.TestCase):
+ def setUp(self):
+ self.tmp_store = tempfile.mktemp()
+
+ def tearDown(self):
+ os.unlink(self.tmp_store)
+
+ 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"))
+
+ data = {'domain': dom.name(), 'event': evStrings[event],
+ 'event_detail': evDetails[event][detail]}
+ _store_event('%s|%s' % (_get_next_event_id(), json.dumps(data)))
+
+ def domain_event_reboot_cb(self, conn, dom, *args):
+ """
+ Callback to handle Domain (VMs) events - VM Reboot.
+ """
+ data = {'domain': dom.name(), 'event': 'Rebooted'}
+ _store_event('%s|%s' % (_get_next_event_id(), json.dumps(data)))
+
+ @unittest.skipUnless(utils.running_as_root(), 'Must be run as root')
+ def test_events_vm_lifecycle(self):
+ inst = model.Model(objstore_loc=self.tmp_store)
+ self.objstore = inst.objstore
+ conn = inst.conn.get()
+
+ # Create a template and VM to test, and start lifecycle tests
+ with RollbackContext() as rollback:
+ # Register the most common Libvirt domain events to be handled.
+ event_map = [(libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
+ self.domain_event_lifecycle_cb),
+ (libvirt.VIR_DOMAIN_EVENT_ID_REBOOT,
+ self.domain_event_reboot_cb)]
+
+ for event, event_cb in event_map:
+ ev_id = conn.domainEventRegisterAny(None, event, event_cb,
+ None)
+ rollback.prependDefer(conn.domainEventDeregisterAny, ev_id)
+
+ # Create a template
+ template_params = {'name': 'ttest',
+ 'source_media': {'type': 'disk',
+ 'path': UBUNTU_ISO}}
+
+ inst.templates_create(template_params)
+ rollback.prependDefer(inst.template_delete, 'ttest')
+
+ # Create a VM (guest)
+ vm_params = {'name': 'kimchi-vm1',
+ 'template': '/plugins/kimchi/templates/ttest'}
+ task = inst.vms_create(vm_params)
+ inst.task_wait(task['id'], 10)
+ task = inst.task_lookup(task['id'])
+ self.assertEquals('finished', task['status'])
+ time.sleep(5)
+ # Check event of domain definition (addition)
+ res = json.loads(_get_event(str(_get_event_id())))
+ self.assertEquals('kimchi-vm1', res['domain'])
+ self.assertEquals('Defined', res['event'])
+ self.assertEquals('Added', res['event_detail'])
+
+ # Start the VM and check the event
+ inst.vm_start('kimchi-vm1')
+ time.sleep(5)
+ res = json.loads(_get_event(str(_get_event_id())))
+ self.assertEquals('kimchi-vm1', res['domain'])
+ self.assertEquals('Started', res['event'])
+ self.assertEquals('Booted', res['event_detail'])
+
+ # Suspend the VM and check the event
+ inst.vm_suspend('kimchi-vm1')
+ time.sleep(5)
+ res = json.loads(_get_event(str(_get_event_id())))
+ self.assertEquals('kimchi-vm1', res['domain'])
+ self.assertEquals('Suspended', res['event'])
+ self.assertEquals('Paused', res['event_detail'])
+
+ # Resume the VM and check the event
+ inst.vm_resume('kimchi-vm1')
+ time.sleep(5)
+ res = json.loads(_get_event(str(_get_event_id())))
+ self.assertEquals('kimchi-vm1', res['domain'])
+ self.assertEquals('Resumed', res['event'])
+ self.assertEquals('Unpaused', res['event_detail'])
+
+ # Reboot the VM and check the event
+ inst.vm_reset('kimchi-vm1')
+ time.sleep(5)
+ res = json.loads(_get_event(str(_get_event_id())))
+ self.assertEquals('kimchi-vm1', res['domain'])
+ self.assertEquals('Rebooted', res['event'])
+
+ # PowerOff (hard stop) the VM and check the event
+ inst.vm_poweroff('kimchi-vm1')
+ time.sleep(5)
+ res = json.loads(_get_event(str(_get_event_id())))
+ self.assertEquals('kimchi-vm1', res['domain'])
+ self.assertEquals('Stopped', res['event'])
+ self.assertEquals('Destroyed', res['event_detail'])
+
+ # Delete the VM and check the event
+ inst.vm_delete('kimchi-vm1')
+ time.sleep(5)
+ res = json.loads(_get_event(str(_get_event_id())))
+ self.assertEquals('kimchi-vm1', res['domain'])
+ self.assertEquals('Undefined', res['event'])
+ self.assertEquals('Removed', res['event_detail'])
--
2.5.5
8 years, 7 months
[PATCH] [Kimchi] Isolate unit tests execution.
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)linux.vnet.ibm.com>
This patch isolates each unit test execution reducing the occurrences of tests
using data from previous unit tests and making results not reliable.
In addition, it also simplifies the output of tests execution, printing the tags
PASSED and FAILED and the error/failed banners only when necessary.
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
tests/run_tests.sh.in | 53 +++++++++++++++++++++++++++++++++++++++------------
1 file changed, 41 insertions(+), 12 deletions(-)
diff --git a/tests/run_tests.sh.in b/tests/run_tests.sh.in
index 68dfa2e..d9c2661 100644
--- a/tests/run_tests.sh.in
+++ b/tests/run_tests.sh.in
@@ -21,6 +21,10 @@
HAVE_UNITTEST=@HAVE_PYMOD_UNITTEST@
PYTHON_VER=@PYTHON_VERSION@
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NC='\033[0m' # No Color
+
if [ "$1" = "-v" ]; then
OPTS="-v"
shift
@@ -40,19 +44,44 @@ else
CMD="python -m unittest"
fi
-LIST=($ARGS)
-MODEL_LIST=()
-MOCK_LIST=()
-for ((i=0;i<${#LIST[@]};i++)); do
-
- if [[ ${LIST[$i]} == test_model* ]]; then
- MODEL_LIST+=(${LIST[$i]})
+NUM_TESTS=0
+TIME=0
+declare -A FAILED_UT # dict to store failed unit tests and its OUTPUT_FILE
+for UT in $ARGS; do
+ OUTPUT_FILE=$(mktemp)
+ echo -n "***** Running unit test: $UT... "
+ # ../../../../../ refers to the project root
+ # ../../../../ refers to wok directory
+ # ../../../ refers to plugins directory
+ PYTHONPATH=../../../../../:../../../../:../../../ \
+ PYTHONWARNINGS="ignore:Unverified HTTPS request" \
+ $CMD $OPTS $UT &> $OUTPUT_FILE
+ RES=$?
+ if [ $RES -ne 0 ]; then
+ # unit test has failed, so keep the OUTPUT_FILE and print the results
+ echo -e "\t ${RED}FAILED${NC}"
+ ERROR_LOG_BEGIN=$(grep -n "^\==*" $OUTPUT_FILE | head -n1 | cut -d":" -f1)
+ ERROR_LOG_END=$(cat $OUTPUT_FILE | wc -l)
+ tail -n $((${ERROR_LOG_END}-${ERROR_LOG_BEGIN}+1)) $OUTPUT_FILE
+ FAILED_UT+=([$UT]=$OUTPUT_FILE)
+ OUTPUT=$(grep "Ran" $OUTPUT_FILE)
else
- MOCK_LIST+=(${LIST[$i]})
+ # unit test has passed, so print the results and delete the OUTPUT_FILE
+ OUTPUT=$(grep "Ran" $OUTPUT_FILE)
+ echo -e "\t ${GREEN}PASSED${NC} - $OUTPUT"
+ rm -rf $OUTPUT_FILE
fi
+ TES=$(echo $OUTPUT | cut -d" " -f2)
+ NUM_TESTS=$(echo "$NUM_TESTS + $TES" | bc)
+ TES=$(echo $OUTPUT | cut -d" " -f5)
+ TIME=$(echo "$TIME + ${TES:0:-1}" | bc)
done
-# ../../../../../ refers to the project root
-# ../../../../ refers to wok directory
-# ../../../ refers to plugins directory
-PYTHONPATH=../../../../../:../../../../:../../../ PYTHONWARNINGS="ignore:Unverified HTTPS request" $CMD $OPTS ${MODEL_LIST[@]} ${MOCK_LIST[@]}
+# Print summary results
+echo -e "======================================================================"
+echo -e "===================== Kimchi Unit Tests Summary ======================"
+echo -e "Ran $NUM_TESTS tests in $TIME seconds."
+for i in "${!FAILED_UT[@]}"; do
+ FAIL_STATS=$(grep FAILED ${FAILED_UT[$i]} | cut -d" " -f2-4)
+ echo -e "$i FAILED: $FAIL_STATS - log available at ${FAILED_UT[$i]}"
+done
--
2.5.5
8 years, 7 months