[PATCH] [WOK] Wok tests
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)linux.vnet.ibm.com>
Moved those tests related to Wok framework from plugins/kimchi to wok structure.
Updated build files and tests to make all test structure usable.
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
Makefile.am | 5 +-
configure.ac | 1 +
plugins/kimchi/tests/test_exception.py | 123 ------------
plugins/kimchi/tests/test_objectstore.py | 97 ---------
plugins/kimchi/tests/test_plugin.py | 126 ------------
plugins/kimchi/tests/test_rollbackcontext.py | 99 ---------
plugins/kimchi/tests/test_server.py | 289 ---------------------------
plugins/kimchi/tests/test_utils.py | 69 -------
tests/Makefile.am | 50 +++++
tests/run_tests.sh.in | 55 +++++
tests/test_config.py.in | 132 ++++++++++++
tests/test_exception.py | 130 ++++++++++++
tests/test_objectstore.py | 97 +++++++++
tests/test_plugin.py | 120 +++++++++++
tests/test_rollbackcontext.py | 99 +++++++++
tests/test_server.py | 282 ++++++++++++++++++++++++++
tests/test_utils.py | 69 +++++++
tests/utils.py | 259 ++++++++++++++++++++++++
18 files changed, 1297 insertions(+), 805 deletions(-)
delete mode 100644 plugins/kimchi/tests/test_exception.py
delete mode 100644 plugins/kimchi/tests/test_objectstore.py
delete mode 100644 plugins/kimchi/tests/test_plugin.py
delete mode 100644 plugins/kimchi/tests/test_rollbackcontext.py
delete mode 100644 plugins/kimchi/tests/test_server.py
delete mode 100644 plugins/kimchi/tests/test_utils.py
create mode 100644 tests/Makefile.am
create mode 100644 tests/run_tests.sh.in
create mode 100644 tests/test_config.py.in
create mode 100644 tests/test_exception.py
create mode 100644 tests/test_objectstore.py
create mode 100644 tests/test_plugin.py
create mode 100644 tests/test_rollbackcontext.py
create mode 100644 tests/test_server.py
create mode 100644 tests/test_utils.py
create mode 100644 tests/utils.py
diff --git a/Makefile.am b/Makefile.am
index c7914d0..1d76260 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,7 +16,8 @@
# 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
-SUBDIRS = src ui docs contrib po plugins
+
+SUBDIRS = src ui docs contrib po plugins tests
man_MANS = docs/wokd.8
@@ -35,7 +36,7 @@ EXTRA_DIST = \
$(NULL)
-PEP8_BLACKLIST = *src/wok/config.py,*src/wok/i18n.py,*plugins/kimchi
+PEP8_BLACKLIST = *src/wok/config.py,*src/wok/i18n.py,*plugins/kimchi,*tests/test_config.py
SKIP_PYFLAKES_ERR = "\./src/wok/websocket\.py"
diff --git a/configure.ac b/configure.ac
index e11a17d..ee139e6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -126,6 +126,7 @@ AC_CONFIG_FILES([
contrib/DEBIAN/control
contrib/wok.spec.fedora
contrib/wok.spec.suse
+ tests/Makefile
],[
chmod +x po/gen-pot
])
diff --git a/plugins/kimchi/tests/test_exception.py b/plugins/kimchi/tests/test_exception.py
deleted file mode 100644
index 4459aa6..0000000
--- a/plugins/kimchi/tests/test_exception.py
+++ /dev/null
@@ -1,123 +0,0 @@
-#
-# Kimchi
-#
-# Copyright IBM, Corp. 2013-2014
-#
-# 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 os
-import unittest
-
-from wok.plugins.kimchi import mockmodel
-
-from utils import get_free_port, patch_auth, request, run_server
-
-
-test_server = None
-model = None
-host = None
-port = None
-ssl_port = None
-
-
-def setup_server(environment='development'):
- global test_server, model, host, port, ssl_port
-
- patch_auth()
- model = mockmodel.MockModel('/tmp/obj-store-test')
- host = '127.0.0.1'
- port = get_free_port('http')
- ssl_port = get_free_port('https')
- test_server = run_server(host, port, ssl_port, test_mode=True, model=model,
- environment=environment)
-
-
-class ExceptionTests(unittest.TestCase):
- def tearDown(self):
- test_server.stop()
- os.unlink('/tmp/obj-store-test')
-
- def test_production_env(self):
- """
- Test reasons sanitized in production env
- """
- setup_server('production')
- # test 404
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms/blah').read()
- )
- self.assertEquals('404 Not Found', resp.get('code'))
-
- # test 405 wrong method
- resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read())
- msg = u'WOKAPI0002E: Delete is not allowed for wokroot'
- self.assertEquals('405 Method Not Allowed', resp.get('code'))
- self.assertEquals(msg, resp.get('reason'))
-
- # test 400 parse error
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms', '{', 'POST').read()
- )
- msg = u'WOKAPI0006E: Unable to parse JSON request'
- self.assertEquals('400 Bad Request', resp.get('code'))
- self.assertEquals(msg, resp.get('reason'))
- self.assertNotIn('call_stack', resp)
-
- # test 400 missing required parameter
- req = json.dumps({})
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms', req, 'POST').read()
- )
- self.assertEquals('400 Bad Request', resp.get('code'))
- m = u"KCHVM0016E: Specify a template to create a virtual machine from"
- self.assertEquals(m, resp.get('reason'))
- self.assertNotIn('call_stack', resp)
-
- def test_development_env(self):
- """
- Test traceback thrown in development env
- """
- setup_server()
- # test 404
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms/blah').read()
- )
- self.assertEquals('404 Not Found', resp.get('code'))
-
- # test 405 wrong method
- resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read())
- msg = u'WOKAPI0002E: Delete is not allowed for wokroot'
- self.assertEquals('405 Method Not Allowed', resp.get('code'))
- self.assertEquals(msg, resp.get('reason'))
-
- # test 400 parse error
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms', '{', 'POST').read()
- )
- msg = u'WOKAPI0006E: Unable to parse JSON request'
- self.assertEquals('400 Bad Request', resp.get('code'))
- self.assertEquals(msg, resp.get('reason'))
- self.assertIn('call_stack', resp)
-
- # test 400 missing required parameter
- req = json.dumps({})
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms', req, 'POST').read()
- )
- m = u"KCHVM0016E: Specify a template to create a virtual machine from"
- self.assertEquals('400 Bad Request', resp.get('code'))
- self.assertEquals(m, resp.get('reason'))
- self.assertIn('call_stack', resp)
diff --git a/plugins/kimchi/tests/test_objectstore.py b/plugins/kimchi/tests/test_objectstore.py
deleted file mode 100644
index 632786f..0000000
--- a/plugins/kimchi/tests/test_objectstore.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2015
-#
-# 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 os
-import tempfile
-import threading
-import unittest
-
-from wok import objectstore
-from wok.exception import NotFoundError
-
-
-tmpfile = None
-
-
-def setUpModule():
- global tmpfile
- tmpfile = tempfile.mktemp()
-
-
-def tearDownModule():
- os.unlink(tmpfile)
-
-
-class ObjectStoreTests(unittest.TestCase):
- def test_objectstore(self):
- store = objectstore.ObjectStore(tmpfile)
-
- with store as session:
- # Test create
- session.store('f����', 't��st1', {'��': 1})
- session.store('f����', 't��st2', {'��': 2})
-
- # Test list
- items = session.get_list('f����')
- self.assertTrue(u't��st1' in items)
- self.assertTrue(u't��st2' in items)
-
- # Test get
- item = session.get('f����', 't��st1')
- self.assertEquals(1, item[u'��'])
-
- # Test delete
- session.delete('f����', 't��st2')
- self.assertEquals(1, len(session.get_list('f����')))
-
- # Test get non-existent item
-
- self.assertRaises(NotFoundError, session.get,
- '��', '��')
-
- # Test delete non-existent item
- self.assertRaises(NotFoundError, session.delete,
- 'f����', 't��st2')
-
- # Test refresh existing item
- session.store('f����', 't��st1', {'��': 2})
- item = session.get('f����', 't��st1')
- self.assertEquals(2, item[u'��'])
-
- def test_object_store_threaded(self):
- def worker(ident):
- with store as session:
- session.store('foo', ident, {})
-
- store = objectstore.ObjectStore(tmpfile)
-
- threads = []
- for i in xrange(50):
- t = threading.Thread(target=worker, args=(i,))
- t.setDaemon(True)
- t.start()
- threads.append(t)
-
- for t in threads:
- t.join()
-
- with store as session:
- self.assertEquals(50, len(session.get_list('foo')))
- self.assertEquals(10, len(store._connections.keys()))
diff --git a/plugins/kimchi/tests/test_plugin.py b/plugins/kimchi/tests/test_plugin.py
deleted file mode 100644
index fc8e277..0000000
--- a/plugins/kimchi/tests/test_plugin.py
+++ /dev/null
@@ -1,126 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013-2014
-#
-# 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 os
-import unittest
-from functools import partial
-
-from wok.utils import get_enabled_plugins
-
-from wok.plugins.kimchi import mockmodel
-
-import utils
-
-
-test_server = None
-model = None
-host = None
-port = None
-ssl_port = None
-
-
-def setUpModule():
- global test_server, model, host, port, ssl_port
-
- utils.patch_auth()
- model = mockmodel.MockModel('/tmp/obj-store-test')
- host = '127.0.0.1'
- port = utils.get_free_port('http')
- ssl_port = utils.get_free_port('https')
- test_server = utils.run_server(host, port, ssl_port, test_mode=True,
- model=model)
-
-
-def tearDownModule():
- test_server.stop()
- os.unlink('/tmp/obj-store-test')
-
-
-(a)unittest.skipUnless(
- 'sample' in [plugin for plugin, _config in get_enabled_plugins()],
- 'sample plugin is not enabled, skip this test!')
-class PluginTests(unittest.TestCase):
-
- def setUp(self):
- self.request = partial(utils.request, host, ssl_port)
-
- def _create_rectangle(self, name, length, width):
- req = json.dumps({'name': name, 'length': length, 'width': width})
- resp = self.request('/plugins/sample/rectangles', req, 'POST')
- return resp
-
- def _get_rectangle(self, name):
- resp = self.request('/plugins/sample/rectangles/%s' % name)
- return json.loads(resp.read())
-
- def _create_rectangle_and_assert(self, name, length, width):
- resp = self._create_rectangle(name, length, width)
- self.assertEquals(201, resp.status)
-
- rectangle = self._get_rectangle(name)
- self.assertEquals(rectangle['name'], name)
- self.assertEquals(rectangle['length'], length)
- self.assertEquals(rectangle['width'], width)
-
- def _get_rectangles_list(self):
- resp = self.request('/plugins/sample/rectangles')
- rectangles = json.loads(resp.read())
- name_list = [rectangle['name'] for rectangle in rectangles]
- return name_list
-
- def test_rectangles(self):
- # Create two new rectangles
- self._create_rectangle_and_assert('small', 10, 8)
- self._create_rectangle_and_assert('big', 20, 16)
-
- # Verify they're in the list
- name_list = self._get_rectangles_list()
- self.assertIn('small', name_list)
- self.assertIn('big', name_list)
-
- # Update the big rectangle.
- req = json.dumps({'length': 40, 'width': 30})
- resp = self.request('/plugins/sample/rectangles/big', req, 'PUT')
- self.assertEquals(200, resp.status)
- big = self._get_rectangle('big')
- self.assertEquals(big['length'], 40)
- self.assertEquals(big['width'], 30)
-
- # Delete two rectangles
- resp = self.request('/plugins/sample/rectangles/big', '{}', 'DELETE')
- self.assertEquals(204, resp.status)
- resp = self.request('/plugins/sample/rectangles/small', '{}', 'DELETE')
- self.assertEquals(204, resp.status)
- name_list = self._get_rectangles_list()
- self.assertEquals([], name_list)
-
- def test_bad_params(self):
- # Bad name
- resp = self._create_rectangle(1.0, 30, 40)
- self.assertEquals(400, resp.status)
-
- # Bad length value
- resp = self._create_rectangle('test', -10.0, 40)
- self.assertEquals(400, resp.status)
-
- # Missing param for width
- req = json.dumps({'name': 'nowidth', 'length': 40})
- resp = self.request('/plugins/sample/rectangles', req, 'POST')
- self.assertEquals(400, resp.status)
diff --git a/plugins/kimchi/tests/test_rollbackcontext.py b/plugins/kimchi/tests/test_rollbackcontext.py
deleted file mode 100644
index 6eac6d0..0000000
--- a/plugins/kimchi/tests/test_rollbackcontext.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2014
-#
-# 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 unittest
-
-from wok.rollbackcontext import RollbackContext
-
-
-class FirstError(Exception):
- '''A hypothetical exception to be raise in the test firstly.'''
- pass
-
-
-class SecondError(Exception):
- '''A hypothetical exception to be raise in the test secondly.'''
- pass
-
-
-class RollbackContextTests(unittest.TestCase):
-
- def setUp(self):
- self._counter = 0
-
- def _inc_counter(self):
- self._counter += 1
-
- def _raise(self, exception=FirstError):
- raise exception()
-
- def test_rollback(self):
- with RollbackContext() as rollback:
- rollback.prependDefer(self._inc_counter)
- rollback.prependDefer(self._inc_counter)
- self.assertEquals(self._counter, 2)
-
- def test_raise(self):
- try:
- with RollbackContext() as rollback:
- rollback.prependDefer(self._inc_counter)
- rollback.prependDefer(self._inc_counter)
- raise FirstError()
- rollback.prependDefer(self._inc_counter)
- except FirstError:
- # All undo before the FirstError should be run
- self.assertEquals(self._counter, 2)
- else:
- self.fail('Should have raised FirstError')
-
- def test_raise_undo(self):
- try:
- with RollbackContext() as rollback:
- rollback.prependDefer(self._inc_counter)
- rollback.prependDefer(self._raise)
- rollback.prependDefer(self._inc_counter)
- except FirstError:
- # All undo should be run
- self.assertEquals(self._counter, 2)
- else:
- self.fail('Should have raised FirstError')
-
- def test_raise_prefer_original(self):
- try:
- with RollbackContext() as rollback:
- rollback.prependDefer(self._raise, SecondError)
- raise FirstError()
- except FirstError:
- pass
- except SecondError:
- self.fail('Should have preferred FirstError to SecondError')
- else:
- self.fail('Should have raised FirstError')
-
- def test_raise_prefer_first_undo(self):
- try:
- with RollbackContext() as rollback:
- rollback.prependDefer(self._raise, SecondError)
- rollback.prependDefer(self._raise, FirstError)
- except FirstError:
- pass
- except SecondError:
- self.fail('Should have preferred FirstError to SecondError')
- else:
- self.fail('Should have raised FirstError')
diff --git a/plugins/kimchi/tests/test_server.py b/plugins/kimchi/tests/test_server.py
deleted file mode 100644
index d5ef565..0000000
--- a/plugins/kimchi/tests/test_server.py
+++ /dev/null
@@ -1,289 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013-2015
-#
-# 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 base64
-import cherrypy
-import json
-import os
-import tempfile
-import threading
-import unittest
-from functools import partial
-
-from wok.control.base import Collection, Resource
-
-from wok.plugins.kimchi import mockmodel
-
-import utils
-
-
-test_server = None
-model = None
-host = None
-port = None
-ssl_port = None
-cherrypy_port = None
-tmpfile = None
-
-
-def setUpModule():
- global test_server, model, host, port, ssl_port, cherrypy_port, tmpfile
-
- utils.patch_auth()
- tmpfile = tempfile.mktemp()
- model = mockmodel.MockModel(tmpfile)
- host = '127.0.0.1'
- port = utils.get_free_port('http')
- ssl_port = utils.get_free_port('https')
- cherrypy_port = utils.get_free_port('cherrypy_port')
- test_server = utils.run_server(host, port, ssl_port, test_mode=True,
- cherrypy_port=cherrypy_port, model=model)
-
-
-def tearDownModule():
- test_server.stop()
- os.unlink(tmpfile)
-
-
-class ServerTests(unittest.TestCase):
- def setUp(self):
- self.request = partial(utils.request, host, ssl_port)
- model.reset()
-
- def assertValidJSON(self, txt):
- try:
- json.loads(txt)
- except ValueError:
- self.fail("Invalid JSON: %s" % txt)
-
- def test_server_start(self):
- """
- Test that we can start a server and receive HTTP:200.
- """
- resp = self.request('/')
- self.assertEquals(200, resp.status)
-
- def test_multithreaded_connection(self):
- def worker():
- for i in xrange(100):
- ret = model.vms_get_list()
- self.assertEquals('test', ret[0])
-
- threads = []
- for i in xrange(100):
- t = threading.Thread(target=worker)
- t.setDaemon(True)
- t.start()
- threads.append(t)
- for t in threads:
- t.join()
-
- def test_collection(self):
- c = Collection(model)
-
- # The base Collection is always empty
- cherrypy.request.method = 'GET'
- cherrypy.request.headers['Accept'] = 'application/json'
- self.assertEquals('[]', c.index())
-
- # POST and DELETE raise HTTP:405 by default
- for method in ('POST', 'DELETE'):
- cherrypy.request.method = method
- try:
- c.index()
- except cherrypy.HTTPError, e:
- self.assertEquals(405, e.code)
- else:
- self.fail("Expected exception not raised")
-
- def test_resource(self):
- r = Resource(model)
-
- # Test the base Resource representation
- cherrypy.request.method = 'GET'
- cherrypy.request.headers['Accept'] = 'application/json'
- self.assertEquals('{}', r.index())
-
- # POST and DELETE raise HTTP:405 by default
- for method in ('POST', 'DELETE'):
- cherrypy.request.method = method
- try:
- r.index()
- except cherrypy.HTTPError, e:
- self.assertEquals(405, e.code)
- else:
- self.fail("Expected exception not raised")
-
- def test_404(self):
- """
- A non-existent path should return HTTP:404
- """
- url_list = ['/plugins/kimchi/doesnotexist', '/plugins/kimchi/vms/blah']
- for url in url_list:
- resp = self.request(url)
- self.assertEquals(404, resp.status)
-
- # Verify it works for DELETE too
- resp = self.request('/plugins/kimchi/templates/blah', '', 'DELETE')
- self.assertEquals(404, resp.status)
-
- def test_accepts(self):
- """
- Verify the following expectations regarding the client Accept header:
- If omitted, default to html
- If 'application/json', serve the rest api
- If 'text/html', serve the UI
- If both of the above (in any order), serve the rest api
- If neither of the above, HTTP:406
- """
- resp = self.request("/", headers={})
- location = resp.getheader('location')
- self.assertTrue(location.endswith("login.html"))
- resp = self.request("/login.html", headers={})
- self.assertTrue('<!doctype html>' in resp.read().lower())
-
- resp = self.request("/", headers={'Accept': 'application/json'})
- self.assertValidJSON(resp.read())
-
- resp = self.request("/", headers={'Accept': 'text/html'})
- location = resp.getheader('location')
- self.assertTrue(location.endswith("login.html"))
-
- resp = self.request("/", headers={'Accept':
- 'application/json, text/html'})
- self.assertValidJSON(resp.read())
-
- resp = self.request("/", headers={'Accept':
- 'text/html, application/json'})
- self.assertValidJSON(resp.read())
-
- h = {'Accept': 'text/plain'}
- resp = self.request('/', None, 'GET', h)
- self.assertEquals(406, resp.status)
-
- def test_auth_unprotected(self):
- hdrs = {'AUTHORIZATION': ''}
- uris = ['/plugins/kimchi/js/kimchi.min.js',
- '/plugins/kimchi/css/theme-default.min.css',
- '/plugins/kimchi/images/icon-vm.png',
- '/libs/jquery-1.10.0.min.js',
- '/login.html',
- '/logout']
-
- for uri in uris:
- resp = self.request(uri, None, 'HEAD', hdrs)
- self.assertEquals(200, resp.status)
-
- def test_auth_protected(self):
- hdrs = {'AUTHORIZATION': ''}
- uris = ['/plugins/kimchi/vms',
- '/plugins/kimchi/vms/doesnotexist',
- '/tasks']
-
- for uri in uris:
- resp = self.request(uri, None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
-
- def test_auth_bad_creds(self):
- # Test HTTPBA
- hdrs = {'AUTHORIZATION': "Basic " + base64.b64encode("nouser:badpass")}
- resp = self.request('/plugins/kimchi/vms', None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
-
- # Test REST API
- hdrs = {'AUTHORIZATION': ''}
- req = json.dumps({'username': 'nouser', 'password': 'badpass'})
- resp = self.request('/login', req, 'POST', hdrs)
- self.assertEquals(401, resp.status)
-
- def test_auth_browser_no_httpba(self):
- # Kimchi detects REST requests from the browser by looking for a
- # specific header
- hdrs = {"X-Requested-With": "XMLHttpRequest"}
-
- # Try our request (Note that request() will add a valid HTTPBA header)
- resp = self.request('/plugins/kimchi/vms', None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
- self.assertEquals(None, resp.getheader('WWW-Authenticate'))
-
- def test_auth_session(self):
- hdrs = {'AUTHORIZATION': '',
- 'Content-Type': 'application/json',
- 'Accept': 'application/json'}
-
- # Test we are logged out
- resp = self.request('/tasks', None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
-
- # Execute a login call
- user, pw = mockmodel.fake_user.items()[0]
- req = json.dumps({'username': user, 'password': pw})
- resp = self.request('/login', req, 'POST', hdrs)
- self.assertEquals(200, resp.status)
-
- user_info = json.loads(resp.read())
- self.assertEquals(sorted(user_info.keys()),
- ['groups', 'roles', 'username'])
- roles = user_info['roles']
- for tab, role in roles.iteritems():
- self.assertEquals(role, u'admin')
-
- cookie = resp.getheader('set-cookie')
- hdrs['Cookie'] = cookie
-
- # Test we are logged in with the cookie
- resp = self.request('/tasks', None, 'GET', hdrs)
- self.assertEquals(200, resp.status)
-
- # Execute a logout call
- resp = self.request('/logout', '{}', 'POST', hdrs)
- self.assertEquals(200, resp.status)
- del hdrs['Cookie']
-
- # Test we are logged out
- resp = self.request('/tasks', None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
-
- def test_get_param(self):
- # Create a mock ISO file
- mockiso = '/tmp/mock.iso'
- open('/tmp/mock.iso', 'w').close()
-
- # Create 2 different templates
- req = json.dumps({'name': 'test-tmpl1', 'cdrom': mockiso})
- self.request('/plugins/kimchi/templates', req, 'POST')
-
- req = json.dumps({'name': 'test-tmpl2', 'cdrom': mockiso})
- self.request('/plugins/kimchi/templates', req, 'POST')
-
- # Remove mock iso
- os.unlink(mockiso)
-
- # Get the templates
- resp = self.request('/plugins/kimchi/templates')
- self.assertEquals(200, resp.status)
- res = json.loads(resp.read())
- self.assertEquals(2, len(res))
-
- # Get a specific template
- resp = self.request('/plugins/kimchi/templates?name=test-tmpl1')
- self.assertEquals(200, resp.status)
- res = json.loads(resp.read())
- self.assertEquals(1, len(res))
- self.assertEquals('test-tmpl1', res[0]['name'])
diff --git a/plugins/kimchi/tests/test_utils.py b/plugins/kimchi/tests/test_utils.py
deleted file mode 100644
index bcb14e2..0000000
--- a/plugins/kimchi/tests/test_utils.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2015
-#
-# 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 unittest
-
-from wok.exception import InvalidParameter
-from wok.utils import convert_data_size
-
-
-class UtilsTests(unittest.TestCase):
- def test_convert_data_size(self):
- failure_data = [{'val': None, 'from': 'MiB'},
- {'val': self, 'from': 'MiB'},
- {'val': 1, 'from': None},
- {'val': 1, 'from': ''},
- {'val': 1, 'from': 'foo'},
- {'val': 1, 'from': 'kib'},
- {'val': 1, 'from': 'MiB', 'to': None},
- {'val': 1, 'from': 'MiB', 'to': ''},
- {'val': 1, 'from': 'MiB', 'to': 'foo'},
- {'val': 1, 'from': 'MiB', 'to': 'kib'}]
-
- for d in failure_data:
- if 'to' in d:
- self.assertRaises(InvalidParameter, convert_data_size,
- d['val'], d['from'], d['to'])
- else:
- self.assertRaises(InvalidParameter, convert_data_size,
- d['val'], d['from'])
-
- success_data = [{'got': convert_data_size(5, 'MiB', 'MiB'),
- 'want': 5},
- {'got': convert_data_size(5, 'MiB', 'KiB'),
- 'want': 5120},
- {'got': convert_data_size(5, 'MiB', 'M'),
- 'want': 5.24288},
- {'got': convert_data_size(5, 'MiB', 'GiB'),
- 'want': 0.0048828125},
- {'got': convert_data_size(5, 'MiB', 'Tb'),
- 'want': 4.194304e-05},
- {'got': convert_data_size(5, 'KiB', 'MiB'),
- 'want': 0.0048828125},
- {'got': convert_data_size(5, 'M', 'MiB'),
- 'want': 4.76837158203125},
- {'got': convert_data_size(5, 'GiB', 'MiB'),
- 'want': 5120},
- {'got': convert_data_size(5, 'Tb', 'MiB'),
- 'want': 596046.4477539062},
- {'got': convert_data_size(5, 'MiB'),
- 'want': convert_data_size(5, 'MiB', 'B')}]
-
- for d in success_data:
- self.assertEquals(d['got'], d['want'])
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..c1f6784
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,50 @@
+#
+# Kimchi
+#
+# Copyright IBM Corp, 2013
+#
+# 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
+
+EXTRA_DIST = \
+ Makefile.am \
+ run_tests.sh.in \
+ test_config.py.in \
+ $(filter-out test_config.py, $(wildcard *.py)) \
+ $(NULL)
+
+noinst_SCRIPTS = run_tests.sh
+
+do_substitution = \
+ sed -e 's,[@]HAVE_PYMOD_UNITTEST[@],$(HAVE_PYMOD_UNITTEST),g' \
+ -e 's,[@]prefix[@],$(prefix),g' \
+ -e 's,[@]datadir[@],$(datadir),g' \
+ -e 's,[@]PYTHON_VERSION[@],$(PYTHON_VERSION),g' \
+ -e 's,[@]wokdir[@],$(pythondir)/wok,g' \
+ -e 's,[@]pkgdatadir[@],$(pkgdatadir),g'
+
+
+run_tests.sh: run_tests.sh.in Makefile
+ $(do_substitution) < $(srcdir)/run_tests.sh.in > run_tests.sh
+ chmod +x run_tests.sh
+
+test_config.py: test_config.py.in Makefile
+ $(do_substitution) < $(srcdir)/test_config.py.in > test_config.py
+
+check-local:
+ $(MKDIR_P) $(top_srcdir)/data/screenshots
+ ./run_tests.sh
+
+BUILT_SOURCES = test_config.py
+CLEANFILES = run_tests.sh test_config.py
diff --git a/tests/run_tests.sh.in b/tests/run_tests.sh.in
new file mode 100644
index 0000000..d1f4b38
--- /dev/null
+++ b/tests/run_tests.sh.in
@@ -0,0 +1,55 @@
+#!/bin/bash
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013-2015
+#
+# 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
+
+HAVE_UNITTEST=@HAVE_PYMOD_UNITTEST@
+PYTHON_VER=@PYTHON_VERSION@
+
+if [ "$1" = "-v" ]; then
+ OPTS="-v"
+ shift
+else
+ OPTS=""
+fi
+
+if [ $# -ne 0 ]; then
+ ARGS="$@"
+else
+ ARGS=`find -name "test_*.py" | xargs -I @ basename @ .py`
+fi
+
+if [ "$HAVE_UNITTEST" != "yes" -o "$PYTHON_VER" == "2.6" ]; then
+ CMD="unit2"
+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]})
+ else
+ MOCK_LIST+=(${LIST[$i]})
+ fi
+done
+
+PYTHONPATH=../src:../ $CMD $OPTS ${MODEL_LIST[@]} ${MOCK_LIST[@]}
diff --git a/tests/test_config.py.in b/tests/test_config.py.in
new file mode 100644
index 0000000..3ef431f
--- /dev/null
+++ b/tests/test_config.py.in
@@ -0,0 +1,132 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2014-2015
+#
+# 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 unittest
+
+from wok.config import Paths, WokConfig
+
+
+get_prefix = None
+
+
+def setUpModule():
+ global get_prefix
+ get_prefix = Paths.get_prefix
+
+
+def tearDownModule():
+ Paths.get_prefix = get_prefix
+
+
+class ConfigTests(unittest.TestCase):
+ def assertInstalledPath(self, actual, expected):
+ if '@pkgdatadir@' != '/usr/share/wok':
+ usr_local = '/usr/local'
+ if not expected.startswith('/usr'):
+ expected = usr_local + expected
+ self.assertEquals(actual, expected)
+
+ def test_installed_paths(self):
+ Paths.get_prefix = lambda self: '@datadir@/wok'
+ paths = Paths()
+ self.assertInstalledPath(paths.state_dir, '/var/lib/wok')
+ self.assertInstalledPath(paths.log_dir, '/var/log/wok')
+ self.assertInstalledPath(paths.conf_dir, '/etc/wok')
+ self.assertInstalledPath(paths.src_dir, '@wokdir@')
+ self.assertInstalledPath(paths.plugins_dir, '@wokdir@/plugins')
+ self.assertInstalledPath(paths.ui_dir, '@datadir@/wok/ui')
+ self.assertInstalledPath(paths.mo_dir, '@prefix@/share/locale')
+
+ def test_uninstalled_paths(self):
+ Paths.get_prefix = lambda self: '/home/user/wok'
+ paths = Paths()
+ self.assertEquals(paths.state_dir, '/home/user/wok/data')
+ self.assertEquals(paths.log_dir, '/home/user/wok/log')
+ self.assertEquals(paths.conf_dir, '/home/user/wok/src')
+ self.assertEquals(paths.src_dir, '/home/user/wok/src/wok')
+ self.assertEquals(paths.plugins_dir, '/home/user/wok/plugins')
+ self.assertEquals(paths.ui_dir, '/home/user/wok/ui')
+ self.assertEquals(paths.mo_dir, '/home/user/wok/mo')
+
+ def test_wok_config(self):
+ Paths.get_prefix = get_prefix
+ paths = Paths()
+ CACHEEXPIRES = 31536000
+ SESSIONSTIMEOUT = 10
+ configObj = {
+ '/': {
+ 'tools.trailing_slash.on': False,
+ 'request.methods_with_bodies': ('POST', 'PUT'),
+ 'tools.nocache.on': True,
+ 'tools.proxy.on': True,
+ 'tools.sessions.on': True,
+ 'tools.sessions.name': 'wok',
+ 'tools.sessions.secure': True,
+ 'tools.sessions.httponly': True,
+ 'tools.sessions.locking': 'explicit',
+ 'tools.sessions.storage_type': 'ram',
+ 'tools.sessions.timeout': SESSIONSTIMEOUT,
+ 'tools.wokauth.on': False
+ },
+ '/wok-ui.html': {
+ 'tools.wokauth.on': True
+ },
+ '/css': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': '%s/ui/css' % paths.prefix,
+ 'tools.expires.on': True,
+ 'tools.expires.secs': CACHEEXPIRES,
+ 'tools.nocache.on': False,
+ 'tools.wokauth.on': False
+ },
+ '/js': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': '%s/ui/js' % paths.prefix,
+ 'tools.expires.on': True,
+ 'tools.expires.secs': CACHEEXPIRES,
+ 'tools.nocache.on': False,
+ 'tools.wokauth.on': False
+ },
+ '/libs': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': '%s/ui/libs' % paths.prefix,
+ 'tools.expires.on': True,
+ 'tools.expires.secs': CACHEEXPIRES,
+ 'tools.nocache.on': False,
+ 'tools.wokauth.on': False
+ },
+ '/images': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': '%s/ui/images' % paths.prefix,
+ 'tools.nocache.on': False,
+ 'tools.wokauth.on': False
+ },
+ '/favicon.ico': {
+ 'tools.staticfile.on': True,
+ 'tools.staticfile.filename':
+ '%s/images/logo.ico' % paths.ui_dir
+ },
+ '/robots.txt': {
+ 'tools.staticfile.on': True,
+ 'tools.staticfile.filename': '%s/robots.txt' % paths.ui_dir
+ },
+ }
+
+ wok_config = WokConfig()
+ self.assertEquals(wok_config, configObj)
diff --git a/tests/test_exception.py b/tests/test_exception.py
new file mode 100644
index 0000000..c0fc11e
--- /dev/null
+++ b/tests/test_exception.py
@@ -0,0 +1,130 @@
+#
+# Kimchi
+#
+# Copyright IBM, Corp. 2013-2014
+#
+# 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 unittest
+
+from utils import get_free_port, patch_auth, request, run_server
+
+
+test_server = None
+model = None
+host = None
+port = None
+ssl_port = None
+
+
+def setup_server(environment='development'):
+ global test_server, model, host, port, ssl_port
+
+ patch_auth()
+ host = '127.0.0.1'
+ port = get_free_port('http')
+ ssl_port = get_free_port('https')
+ test_server = run_server(host, port, ssl_port, test_mode=True,
+ environment=environment)
+
+
+class ExceptionTests(unittest.TestCase):
+ def tearDown(self):
+ test_server.stop()
+
+ def test_production_env(self):
+ """
+ Test reasons sanitized in production env
+ """
+ setup_server('production')
+
+ # test 404
+ resp = json.loads(request(host, ssl_port, '/tasks/blah').read())
+ self.assertEquals('404 Not Found', resp.get('code'))
+
+ # test 405 wrong method
+ resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read())
+ msg = u'WOKAPI0002E: Delete is not allowed for wokroot'
+ self.assertEquals('405 Method Not Allowed', resp.get('code'))
+ self.assertEquals(msg, resp.get('reason'))
+
+ # test 400 parse error
+ resp = json.loads(request(host, ssl_port, '/tasks', '{',
+ 'POST').read())
+ msg = u'WOKAPI0006E: Unable to parse JSON request'
+ self.assertEquals('400 Bad Request', resp.get('code'))
+ self.assertEquals(msg, resp.get('reason'))
+ self.assertNotIn('call_stack', resp)
+
+ # test 400 missing required parameter
+ # TODO: need add this test when some REST API from wok accepts POST
+# req = json.dumps({})
+# resp = json.loads(request(host, ssl_port, '/tasks', req,
+# 'POST').read())
+# self.assertEquals('400 Bad Request', resp.get('code'))
+# m = u"KCHVM0016E: Specify a template to create a virtual machine from"
+# self.assertEquals(m, resp.get('reason'))
+# self.assertNotIn('call_stack', resp)
+
+ # test 405 method not allowed
+ req = json.dumps({})
+ resp = json.loads(request(host, ssl_port, '/tasks', req,
+ 'POST').read())
+ m = u"WOKAPI0005E: Create is not allowed for tasks"
+ self.assertEquals('405 Method Not Allowed', resp.get('code'))
+ self.assertEquals(m, resp.get('reason'))
+
+ def test_development_env(self):
+ """
+ Test traceback thrown in development env
+ """
+ setup_server()
+ # test 404
+ resp = json.loads(request(host, ssl_port, '/tasks/blah').read())
+ self.assertEquals('404 Not Found', resp.get('code'))
+
+ # test 405 wrong method
+ resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read())
+ msg = u'WOKAPI0002E: Delete is not allowed for wokroot'
+ self.assertEquals('405 Method Not Allowed', resp.get('code'))
+ self.assertEquals(msg, resp.get('reason'))
+
+ # test 400 parse error
+ resp = json.loads(request(host, ssl_port, '/tasks', '{',
+ 'POST').read())
+ msg = u'WOKAPI0006E: Unable to parse JSON request'
+ self.assertEquals('400 Bad Request', resp.get('code'))
+ self.assertEquals(msg, resp.get('reason'))
+ self.assertIn('call_stack', resp)
+
+ # test 400 missing required parameter
+ # TODO: need add this test when some REST API from wok accepts POST
+# req = json.dumps({})
+# resp = json.loads(request(host, ssl_port, '/tasks', req,
+# 'POST').read())
+# m = u"KCHVM0016E: Specify a template to create a virtual machine from"
+# self.assertEquals('400 Bad Request', resp.get('code'))
+# self.assertEquals(m, resp.get('reason'))
+# self.assertIn('call_stack', resp)
+
+ # test 405 method not allowed
+ req = json.dumps({})
+ resp = json.loads(request(host, ssl_port, '/tasks', req,
+ 'POST').read())
+ m = u"WOKAPI0005E: Create is not allowed for tasks"
+ self.assertEquals('405 Method Not Allowed', resp.get('code'))
+ self.assertEquals(m, resp.get('reason'))
+ self.assertIn('call_stack', resp)
diff --git a/tests/test_objectstore.py b/tests/test_objectstore.py
new file mode 100644
index 0000000..632786f
--- /dev/null
+++ b/tests/test_objectstore.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2015
+#
+# 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 os
+import tempfile
+import threading
+import unittest
+
+from wok import objectstore
+from wok.exception import NotFoundError
+
+
+tmpfile = None
+
+
+def setUpModule():
+ global tmpfile
+ tmpfile = tempfile.mktemp()
+
+
+def tearDownModule():
+ os.unlink(tmpfile)
+
+
+class ObjectStoreTests(unittest.TestCase):
+ def test_objectstore(self):
+ store = objectstore.ObjectStore(tmpfile)
+
+ with store as session:
+ # Test create
+ session.store('f����', 't��st1', {'��': 1})
+ session.store('f����', 't��st2', {'��': 2})
+
+ # Test list
+ items = session.get_list('f����')
+ self.assertTrue(u't��st1' in items)
+ self.assertTrue(u't��st2' in items)
+
+ # Test get
+ item = session.get('f����', 't��st1')
+ self.assertEquals(1, item[u'��'])
+
+ # Test delete
+ session.delete('f����', 't��st2')
+ self.assertEquals(1, len(session.get_list('f����')))
+
+ # Test get non-existent item
+
+ self.assertRaises(NotFoundError, session.get,
+ '��', '��')
+
+ # Test delete non-existent item
+ self.assertRaises(NotFoundError, session.delete,
+ 'f����', 't��st2')
+
+ # Test refresh existing item
+ session.store('f����', 't��st1', {'��': 2})
+ item = session.get('f����', 't��st1')
+ self.assertEquals(2, item[u'��'])
+
+ def test_object_store_threaded(self):
+ def worker(ident):
+ with store as session:
+ session.store('foo', ident, {})
+
+ store = objectstore.ObjectStore(tmpfile)
+
+ threads = []
+ for i in xrange(50):
+ t = threading.Thread(target=worker, args=(i,))
+ t.setDaemon(True)
+ t.start()
+ threads.append(t)
+
+ for t in threads:
+ t.join()
+
+ with store as session:
+ self.assertEquals(50, len(session.get_list('foo')))
+ self.assertEquals(10, len(store._connections.keys()))
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
new file mode 100644
index 0000000..5d371e5
--- /dev/null
+++ b/tests/test_plugin.py
@@ -0,0 +1,120 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013-2014
+#
+# 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 unittest
+from functools import partial
+
+from wok.utils import get_enabled_plugins
+
+import utils
+
+
+test_server = None
+model = None
+host = None
+port = None
+ssl_port = None
+
+
+def setUpModule():
+ global test_server, model, host, port, ssl_port
+
+ utils.patch_auth()
+ host = '127.0.0.1'
+ port = utils.get_free_port('http')
+ ssl_port = utils.get_free_port('https')
+ test_server = utils.run_server(host, port, ssl_port, test_mode=True)
+
+
+def tearDownModule():
+ test_server.stop()
+
+
+(a)unittest.skipUnless(
+ 'sample' in [plugin for plugin, _config in get_enabled_plugins()],
+ 'sample plugin is not enabled, skip this test!')
+class PluginTests(unittest.TestCase):
+
+ def setUp(self):
+ self.request = partial(utils.request, host, ssl_port)
+
+ def _create_rectangle(self, name, length, width):
+ req = json.dumps({'name': name, 'length': length, 'width': width})
+ resp = self.request('/plugins/sample/rectangles', req, 'POST')
+ return resp
+
+ def _get_rectangle(self, name):
+ resp = self.request('/plugins/sample/rectangles/%s' % name)
+ return json.loads(resp.read())
+
+ def _create_rectangle_and_assert(self, name, length, width):
+ resp = self._create_rectangle(name, length, width)
+ self.assertEquals(201, resp.status)
+
+ rectangle = self._get_rectangle(name)
+ self.assertEquals(rectangle['name'], name)
+ self.assertEquals(rectangle['length'], length)
+ self.assertEquals(rectangle['width'], width)
+
+ def _get_rectangles_list(self):
+ resp = self.request('/plugins/sample/rectangles')
+ rectangles = json.loads(resp.read())
+ name_list = [rectangle['name'] for rectangle in rectangles]
+ return name_list
+
+ def test_rectangles(self):
+ # Create two new rectangles
+ self._create_rectangle_and_assert('small', 10, 8)
+ self._create_rectangle_and_assert('big', 20, 16)
+
+ # Verify they're in the list
+ name_list = self._get_rectangles_list()
+ self.assertIn('small', name_list)
+ self.assertIn('big', name_list)
+
+ # Update the big rectangle.
+ req = json.dumps({'length': 40, 'width': 30})
+ resp = self.request('/plugins/sample/rectangles/big', req, 'PUT')
+ self.assertEquals(200, resp.status)
+ big = self._get_rectangle('big')
+ self.assertEquals(big['length'], 40)
+ self.assertEquals(big['width'], 30)
+
+ # Delete two rectangles
+ resp = self.request('/plugins/sample/rectangles/big', '{}', 'DELETE')
+ self.assertEquals(204, resp.status)
+ resp = self.request('/plugins/sample/rectangles/small', '{}', 'DELETE')
+ self.assertEquals(204, resp.status)
+ name_list = self._get_rectangles_list()
+ self.assertEquals([], name_list)
+
+ def test_bad_params(self):
+ # Bad name
+ resp = self._create_rectangle(1.0, 30, 40)
+ self.assertEquals(400, resp.status)
+
+ # Bad length value
+ resp = self._create_rectangle('test', -10.0, 40)
+ self.assertEquals(400, resp.status)
+
+ # Missing param for width
+ req = json.dumps({'name': 'nowidth', 'length': 40})
+ resp = self.request('/plugins/sample/rectangles', req, 'POST')
+ self.assertEquals(400, resp.status)
diff --git a/tests/test_rollbackcontext.py b/tests/test_rollbackcontext.py
new file mode 100644
index 0000000..6eac6d0
--- /dev/null
+++ b/tests/test_rollbackcontext.py
@@ -0,0 +1,99 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2014
+#
+# 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 unittest
+
+from wok.rollbackcontext import RollbackContext
+
+
+class FirstError(Exception):
+ '''A hypothetical exception to be raise in the test firstly.'''
+ pass
+
+
+class SecondError(Exception):
+ '''A hypothetical exception to be raise in the test secondly.'''
+ pass
+
+
+class RollbackContextTests(unittest.TestCase):
+
+ def setUp(self):
+ self._counter = 0
+
+ def _inc_counter(self):
+ self._counter += 1
+
+ def _raise(self, exception=FirstError):
+ raise exception()
+
+ def test_rollback(self):
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._inc_counter)
+ rollback.prependDefer(self._inc_counter)
+ self.assertEquals(self._counter, 2)
+
+ def test_raise(self):
+ try:
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._inc_counter)
+ rollback.prependDefer(self._inc_counter)
+ raise FirstError()
+ rollback.prependDefer(self._inc_counter)
+ except FirstError:
+ # All undo before the FirstError should be run
+ self.assertEquals(self._counter, 2)
+ else:
+ self.fail('Should have raised FirstError')
+
+ def test_raise_undo(self):
+ try:
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._inc_counter)
+ rollback.prependDefer(self._raise)
+ rollback.prependDefer(self._inc_counter)
+ except FirstError:
+ # All undo should be run
+ self.assertEquals(self._counter, 2)
+ else:
+ self.fail('Should have raised FirstError')
+
+ def test_raise_prefer_original(self):
+ try:
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._raise, SecondError)
+ raise FirstError()
+ except FirstError:
+ pass
+ except SecondError:
+ self.fail('Should have preferred FirstError to SecondError')
+ else:
+ self.fail('Should have raised FirstError')
+
+ def test_raise_prefer_first_undo(self):
+ try:
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._raise, SecondError)
+ rollback.prependDefer(self._raise, FirstError)
+ except FirstError:
+ pass
+ except SecondError:
+ self.fail('Should have preferred FirstError to SecondError')
+ else:
+ self.fail('Should have raised FirstError')
diff --git a/tests/test_server.py b/tests/test_server.py
new file mode 100644
index 0000000..636591d
--- /dev/null
+++ b/tests/test_server.py
@@ -0,0 +1,282 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013-2015
+#
+# 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 base64
+import cherrypy
+import json
+import tempfile
+import threading
+import unittest
+from functools import partial
+
+from wok.control.base import Collection, Resource
+
+import utils
+
+
+test_server = None
+model = None
+host = None
+port = None
+ssl_port = None
+cherrypy_port = None
+tmpfile = None
+
+
+def setUpModule():
+ global test_server, model, host, port, ssl_port, cherrypy_port, tmpfile
+
+ utils.patch_auth()
+ tmpfile = tempfile.mktemp()
+ host = '127.0.0.1'
+ port = utils.get_free_port('http')
+ ssl_port = utils.get_free_port('https')
+ cherrypy_port = utils.get_free_port('cherrypy_port')
+ test_server = utils.run_server(host, port, ssl_port, test_mode=True,
+ cherrypy_port=cherrypy_port)
+
+
+def tearDownModule():
+ test_server.stop()
+
+
+class ServerTests(unittest.TestCase):
+ def setUp(self):
+ self.request = partial(utils.request, host, ssl_port)
+
+ def assertValidJSON(self, txt):
+ try:
+ json.loads(txt)
+ except ValueError:
+ self.fail("Invalid JSON: %s" % txt)
+
+ def test_server_start(self):
+ """
+ Test that we can start a server and receive HTTP:200.
+ """
+ resp = self.request('/')
+ self.assertEquals(200, resp.status)
+
+ def test_multithreaded_connection(self):
+ def worker():
+ for i in xrange(100):
+ ret = ['test']
+ self.assertEquals('test', ret[0])
+
+ threads = []
+ for i in xrange(100):
+ t = threading.Thread(target=worker)
+ t.setDaemon(True)
+ t.start()
+ threads.append(t)
+ for t in threads:
+ t.join()
+
+ def test_collection(self):
+ c = Collection(model)
+
+ # The base Collection is always empty
+ cherrypy.request.method = 'GET'
+ cherrypy.request.headers['Accept'] = 'application/json'
+ self.assertEquals('[]', c.index())
+
+ # POST and DELETE raise HTTP:405 by default
+ for method in ('POST', 'DELETE'):
+ cherrypy.request.method = method
+ try:
+ c.index()
+ except cherrypy.HTTPError, e:
+ self.assertEquals(405, e.code)
+ else:
+ self.fail("Expected exception not raised")
+
+ def test_resource(self):
+ r = Resource(model)
+
+ # Test the base Resource representation
+ cherrypy.request.method = 'GET'
+ cherrypy.request.headers['Accept'] = 'application/json'
+ self.assertEquals('{}', r.index())
+
+ # POST and DELETE raise HTTP:405 by default
+ for method in ('POST', 'DELETE'):
+ cherrypy.request.method = method
+ try:
+ r.index()
+ except cherrypy.HTTPError, e:
+ self.assertEquals(405, e.code)
+ else:
+ self.fail("Expected exception not raised")
+
+ def test_404(self):
+ """
+ A non-existent path should return HTTP:404
+ """
+ url_list = ['/doesnotexist', '/tasks/blah']
+ for url in url_list:
+ resp = self.request(url)
+ self.assertEquals(404, resp.status)
+
+ # Verify it works for DELETE too
+ resp = self.request('/tasks/blah', '', 'DELETE')
+ self.assertEquals(404, resp.status)
+
+ def test_accepts(self):
+ """
+ Verify the following expectations regarding the client Accept header:
+ If omitted, default to html
+ If 'application/json', serve the rest api
+ If 'text/html', serve the UI
+ If both of the above (in any order), serve the rest api
+ If neither of the above, HTTP:406
+ """
+ resp = self.request("/", headers={})
+ location = resp.getheader('location')
+ self.assertTrue(location.endswith("login.html"))
+ resp = self.request("/login.html", headers={})
+ self.assertTrue('<!doctype html>' in resp.read().lower())
+
+ resp = self.request("/", headers={'Accept': 'application/json'})
+ self.assertValidJSON(resp.read())
+
+ resp = self.request("/", headers={'Accept': 'text/html'})
+ location = resp.getheader('location')
+ self.assertTrue(location.endswith("login.html"))
+
+ resp = self.request("/", headers={'Accept':
+ 'application/json, text/html'})
+ self.assertValidJSON(resp.read())
+
+ resp = self.request("/", headers={'Accept':
+ 'text/html, application/json'})
+ self.assertValidJSON(resp.read())
+
+ h = {'Accept': 'text/plain'}
+ resp = self.request('/', None, 'GET', h)
+ self.assertEquals(406, resp.status)
+
+ def test_auth_unprotected(self):
+ hdrs = {'AUTHORIZATION': ''}
+ uris = ['/js/wok.min.js',
+ '/css/theme-default.min.css',
+ '/images/favicon.png',
+ '/libs/jquery/jquery.min.js',
+ '/login.html',
+ '/logout']
+
+ for uri in uris:
+ resp = self.request(uri, None, 'HEAD', hdrs)
+ self.assertEquals(200, resp.status)
+
+ def test_auth_protected(self):
+ hdrs = {'AUTHORIZATION': ''}
+ uris = ['/tasks']
+
+ for uri in uris:
+ resp = self.request(uri, None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+
+ def test_auth_bad_creds(self):
+ # Test HTTPBA
+ hdrs = {'AUTHORIZATION': "Basic " + base64.b64encode("nouser:badpass")}
+ resp = self.request('/tasks', None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+
+ # Test REST API
+ hdrs = {'AUTHORIZATION': ''}
+ req = json.dumps({'username': 'nouser', 'password': 'badpass'})
+ resp = self.request('/login', req, 'POST', hdrs)
+ self.assertEquals(401, resp.status)
+
+ def test_auth_browser_no_httpba(self):
+ # Kimchi detects REST requests from the browser by looking for a
+ # specific header
+ hdrs = {"X-Requested-With": "XMLHttpRequest"}
+
+ # Try our request (Note that request() will add a valid HTTPBA header)
+ resp = self.request('/tasks', None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+ self.assertEquals(None, resp.getheader('WWW-Authenticate'))
+
+ def test_auth_session(self):
+ hdrs = {'AUTHORIZATION': '',
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'}
+
+ # Test we are logged out
+ resp = self.request('/tasks', None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+
+ # Execute a login call
+ user, pw = utils.fake_user.items()[0]
+ req = json.dumps({'username': user, 'password': pw})
+ resp = self.request('/login', req, 'POST', hdrs)
+ self.assertEquals(200, resp.status)
+
+ user_info = json.loads(resp.read())
+ self.assertEquals(sorted(user_info.keys()),
+ ['groups', 'roles', 'username'])
+ roles = user_info['roles']
+ for tab, role in roles.iteritems():
+ self.assertEquals(role, u'admin')
+
+ cookie = resp.getheader('set-cookie')
+ hdrs['Cookie'] = cookie
+
+ # Test we are logged in with the cookie
+ resp = self.request('/tasks', None, 'GET', hdrs)
+ self.assertEquals(200, resp.status)
+
+ # Execute a logout call
+ resp = self.request('/logout', '{}', 'POST', hdrs)
+ self.assertEquals(200, resp.status)
+ del hdrs['Cookie']
+
+ # Test we are logged out
+ resp = self.request('/tasks', None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+
+ # TODO: uncomment and adapt when some wok API accepts parameters to test
+# def test_get_param(self):
+# # Create a mock ISO file
+# mockiso = '/tmp/mock.iso'
+# open('/tmp/mock.iso', 'w').close()
+#
+# # Create 2 different templates
+# req = json.dumps({'name': 'test-tmpl1', 'cdrom': mockiso})
+# self.request('/plugins/kimchi/templates', req, 'POST')
+#
+# req = json.dumps({'name': 'test-tmpl2', 'cdrom': mockiso})
+# self.request('/plugins/kimchi/templates', req, 'POST')
+#
+# # Remove mock iso
+# os.unlink(mockiso)
+#
+# # Get the templates
+# resp = self.request('/plugins/kimchi/templates')
+# self.assertEquals(200, resp.status)
+# res = json.loads(resp.read())
+# self.assertEquals(2, len(res))
+#
+# # Get a specific template
+# resp = self.request('/plugins/kimchi/templates?name=test-tmpl1')
+# self.assertEquals(200, resp.status)
+# res = json.loads(resp.read())
+# self.assertEquals(1, len(res))
+# self.assertEquals('test-tmpl1', res[0]['name'])
diff --git a/tests/test_utils.py b/tests/test_utils.py
new file mode 100644
index 0000000..bcb14e2
--- /dev/null
+++ b/tests/test_utils.py
@@ -0,0 +1,69 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2015
+#
+# 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 unittest
+
+from wok.exception import InvalidParameter
+from wok.utils import convert_data_size
+
+
+class UtilsTests(unittest.TestCase):
+ def test_convert_data_size(self):
+ failure_data = [{'val': None, 'from': 'MiB'},
+ {'val': self, 'from': 'MiB'},
+ {'val': 1, 'from': None},
+ {'val': 1, 'from': ''},
+ {'val': 1, 'from': 'foo'},
+ {'val': 1, 'from': 'kib'},
+ {'val': 1, 'from': 'MiB', 'to': None},
+ {'val': 1, 'from': 'MiB', 'to': ''},
+ {'val': 1, 'from': 'MiB', 'to': 'foo'},
+ {'val': 1, 'from': 'MiB', 'to': 'kib'}]
+
+ for d in failure_data:
+ if 'to' in d:
+ self.assertRaises(InvalidParameter, convert_data_size,
+ d['val'], d['from'], d['to'])
+ else:
+ self.assertRaises(InvalidParameter, convert_data_size,
+ d['val'], d['from'])
+
+ success_data = [{'got': convert_data_size(5, 'MiB', 'MiB'),
+ 'want': 5},
+ {'got': convert_data_size(5, 'MiB', 'KiB'),
+ 'want': 5120},
+ {'got': convert_data_size(5, 'MiB', 'M'),
+ 'want': 5.24288},
+ {'got': convert_data_size(5, 'MiB', 'GiB'),
+ 'want': 0.0048828125},
+ {'got': convert_data_size(5, 'MiB', 'Tb'),
+ 'want': 4.194304e-05},
+ {'got': convert_data_size(5, 'KiB', 'MiB'),
+ 'want': 0.0048828125},
+ {'got': convert_data_size(5, 'M', 'MiB'),
+ 'want': 4.76837158203125},
+ {'got': convert_data_size(5, 'GiB', 'MiB'),
+ 'want': 5120},
+ {'got': convert_data_size(5, 'Tb', 'MiB'),
+ 'want': 596046.4477539062},
+ {'got': convert_data_size(5, 'MiB'),
+ 'want': convert_data_size(5, 'MiB', 'B')}]
+
+ for d in success_data:
+ self.assertEquals(d['got'], d['want'])
diff --git a/tests/utils.py b/tests/utils.py
new file mode 100644
index 0000000..1c6ee12
--- /dev/null
+++ b/tests/utils.py
@@ -0,0 +1,259 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013-2015
+#
+# 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 base64
+import cherrypy
+import grp
+import httplib
+import inspect
+import json
+import os
+import socket
+import ssl
+import sys
+import threading
+import time
+import unittest
+from contextlib import closing
+from lxml import etree
+
+import wok.server
+from wok.config import config, PluginPaths
+from wok.auth import User, USER_NAME, USER_GROUPS, USER_ROLES, tabs
+from wok.exception import NotFoundError, OperationFailed
+from wok.utils import wok_log
+
+
+_ports = {}
+fake_user = {'test': 'passw0rd'}
+
+# provide missing unittest decorators and API for python 2.6; these decorators
+# do not actually work, just avoid the syntax failure
+if sys.version_info[:2] == (2, 6):
+ def skipUnless(condition, reason):
+ if not condition:
+ sys.stderr.write('[expected failure] ')
+ raise Exception(reason)
+ return lambda obj: obj
+
+ unittest.skipUnless = skipUnless
+ unittest.expectedFailure = lambda obj: obj
+
+ def assertGreater(self, a, b, msg=None):
+ if not a > b:
+ self.fail('%s not greater than %s' % (repr(a), repr(b)))
+
+ def assertGreaterEqual(self, a, b, msg=None):
+ if not a >= b:
+ self.fail('%s not greater than or equal to %s'
+ % (repr(a), repr(b)))
+
+ def assertIsInstance(self, obj, cls, msg=None):
+ if not isinstance(obj, cls):
+ self.fail('%s is not an instance of %r' % (repr(obj), cls))
+
+ def assertIn(self, a, b, msg=None):
+ if a not in b:
+ self.fail("%s is not in %b" % (repr(a), repr(b)))
+
+ def assertNotIn(self, a, b, msg=None):
+ if a in b:
+ self.fail("%s is in %b" % (repr(a), repr(b)))
+
+ unittest.TestCase.assertGreaterEqual = assertGreaterEqual
+ unittest.TestCase.assertGreater = assertGreater
+ unittest.TestCase.assertIsInstance = assertIsInstance
+ unittest.TestCase.assertIn = assertIn
+ unittest.TestCase.assertNotIn = assertNotIn
+
+
+def get_free_port(name='http'):
+ global _ports
+ if _ports.get(name) is not None:
+ return _ports[name]
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ with closing(sock):
+ try:
+ sock.bind(("0.0.0.0", 0))
+ except:
+ raise Exception("Could not find a free port")
+ _ports[name] = sock.getsockname()[1]
+ return _ports[name]
+
+
+def run_server(host, port, ssl_port, test_mode, cherrypy_port=None,
+ model=None, environment='development'):
+
+ if cherrypy_port is None:
+ cherrypy_port = get_free_port('cherrypy_port')
+
+ if ssl_port is None:
+ ssl_port = get_free_port('https')
+
+ args = type('_', (object,),
+ {'host': host, 'port': port, 'ssl_port': ssl_port,
+ 'cherrypy_port': cherrypy_port, 'max_body_size': '4*1024',
+ 'ssl_cert': '', 'ssl_key': '',
+ 'test': test_mode, 'access_log': '/dev/null',
+ 'error_log': '/dev/null', 'environment': environment,
+ 'log_level': 'debug'})()
+ if model is not None:
+ setattr(args, 'model', model)
+
+ s = wok.server.Server(args)
+ t = threading.Thread(target=s.start)
+ t.setDaemon(True)
+ t.start()
+ cherrypy.engine.wait(cherrypy.engine.states.STARTED)
+ return s
+
+
+def silence_server():
+ """
+ Silence server status messages on stdout
+ """
+ cherrypy.config.update({"environment": "embedded"})
+
+
+def running_as_root():
+ return os.geteuid() == 0
+
+
+def _request(conn, path, data, method, headers):
+ if headers is None:
+ headers = {'Content-Type': 'application/json',
+ 'Accept': 'application/json'}
+ if 'AUTHORIZATION' not in headers.keys():
+ user, pw = fake_user.items()[0]
+ hdr = "Basic " + base64.b64encode("%s:%s" % (user, pw))
+ headers['AUTHORIZATION'] = hdr
+ conn.request(method, path, data, headers)
+ return conn.getresponse()
+
+
+def request(host, port, path, data=None, method='GET', headers=None):
+ # verify if HTTPSConnection has context parameter
+ if "context" in inspect.getargspec(httplib.HTTPSConnection.__init__).args:
+ context = ssl._create_unverified_context()
+ conn = httplib.HTTPSConnection(host, port, context=context)
+ else:
+ conn = httplib.HTTPSConnection(host, port)
+
+ return _request(conn, path, data, method, headers)
+
+
+def get_remote_iso_path():
+ """
+ Get a remote iso with the right arch from the distro files shipped
+ with kimchi.
+ """
+ host_arch = os.uname()[4]
+ remote_path = ''
+ with open(os.path.join(PluginPaths('kimchi').conf_dir, 'distros.d',
+ 'fedora.json')) as fedora_isos:
+ # Get a list of dicts
+ json_isos_list = json.load(fedora_isos)
+ for iso in json_isos_list:
+ if (iso.get('os_arch')) == host_arch:
+ remote_path = iso.get('path')
+ break
+
+ return remote_path
+
+
+class FakeUser(User):
+ auth_type = "fake"
+ sudo = True
+
+ def __init__(self, username):
+ self.user = {}
+ self.user[USER_NAME] = username
+ self.user[USER_GROUPS] = None
+ self.user[USER_ROLES] = dict.fromkeys(tabs, 'user')
+
+ def get_groups(self):
+ return sorted([group.gr_name for group in grp.getgrall()])[0:3]
+
+ def get_roles(self):
+ if self.sudo:
+ self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin')
+ return self.user[USER_ROLES]
+
+ def get_user(self):
+ return self.user
+
+ @staticmethod
+ def authenticate(username, password, service="passwd"):
+ try:
+ return fake_user[username] == password
+ except KeyError, e:
+ raise OperationFailed("WOKAUTH0001E", {'username': 'username',
+ 'code': e.message})
+
+
+def patch_auth(sudo=True):
+ """
+ Override the authenticate function with a simple test against an
+ internal dict of users and passwords.
+ """
+ config.set("authentication", "method", "fake")
+ FakeUser.sudo = sudo
+
+
+def normalize_xml(xml_str):
+ return etree.tostring(etree.fromstring(xml_str,
+ etree.XMLParser(remove_blank_text=True)))
+
+
+def wait_task(task_lookup, taskid, timeout=10):
+ for i in range(0, timeout):
+ task_info = task_lookup(taskid)
+ if task_info['status'] == "running":
+ wok_log.info("Waiting task %s, message: %s",
+ taskid, task_info['message'])
+ time.sleep(1)
+ else:
+ return
+ wok_log.error("Timeout while process long-run task, "
+ "try to increase timeout value.")
+
+
+# The action functions in model backend raise NotFoundError exception if the
+# element is not found. But in some tests, these functions are called after
+# the element has been deleted if test finishes correctly, then NofFoundError
+# exception is raised and rollback breaks. To avoid it, this wrapper ignores
+# the NotFoundError.
+def rollback_wrapper(func, resource, *args):
+ try:
+ func(resource, *args)
+ except NotFoundError:
+ # VM has been deleted already
+ return
+
+
+# This function is used to test storage volume upload.
+# If we use self.request, we may encode multipart formdata by ourselves
+# requests lib take care of encode part, so use this lib instead
+def fake_auth_header():
+ headers = {'Accept': 'application/json'}
+ user, pw = fake_user.items()[0]
+ hdr = "Basic " + base64.b64encode("%s:%s" % (user, pw))
+ headers['AUTHORIZATION'] = hdr
+ return headers
--
2.4.3
9 years, 2 months
[PATCH] [WOK] Move KCHASYNC0003E to Wok and update code.
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)linux.vnet.ibm.com>
Move the KCHASYNC0003E ("Timeout of %(seconds)s seconds expired while running
task '%(task)s.") message error from Kimchi to Wok and update its code to
WOKASYNC0003E.
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
plugins/kimchi/i18n.py | 2 --
src/wok/i18n.py | 2 ++
src/wok/model/tasks.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/plugins/kimchi/i18n.py b/plugins/kimchi/i18n.py
index 253f00d..23b6f96 100644
--- a/plugins/kimchi/i18n.py
+++ b/plugins/kimchi/i18n.py
@@ -25,8 +25,6 @@ _ = gettext.gettext
messages = {
"KCHAPI0001E": _("Unknown parameter %(value)s"),
- "KCHASYNC0003E": _("Timeout of %(seconds)s seconds expired while running task '%(task)s."),
-
"KCHAUTH0004E": _("User %(user_id)s not found with given LDAP settings."),
"KCHDEVS0001E": _('Unknown "_cap" specified'),
diff --git a/src/wok/i18n.py b/src/wok/i18n.py
index 43a1edf..a6376bd 100644
--- a/src/wok/i18n.py
+++ b/src/wok/i18n.py
@@ -33,6 +33,8 @@ messages = {
"WOKASYNC0001E": _("Datastore is not initiated in the model object."),
"WOKASYNC0002E": _("Unable to start task due error: %(err)s"),
+ "WOKASYNC0003E": _("Timeout of %(seconds)s seconds expired while running task '%(task)s."),
+
"WOKAUTH0001E": _("Authentication failed for user '%(username)s'. [Error code: %(code)s]"),
"WOKAUTH0002E": _("You are not authorized to access Kimchi"),
diff --git a/src/wok/model/tasks.py b/src/wok/model/tasks.py
index 678fdc2..d57c908 100644
--- a/src/wok/model/tasks.py
+++ b/src/wok/model/tasks.py
@@ -60,5 +60,5 @@ class TaskModel(object):
time.sleep(1)
- raise TimeoutExpired('KCHASYNC0003E', {'seconds': timeout,
+ raise TimeoutExpired('WOKASYNC0003E', {'seconds': timeout,
'task': task['target_uri']})
--
2.4.3
9 years, 2 months
[PATCH] Move remaining Kimchi tests to src/wok structure.
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)linux.vnet.ibm.com>
Moving remaining Kimchi tests from old plugins structure to new src/wok structure.
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
plugins/kimchi/tests/test_exception.py | 123 ---------
plugins/kimchi/tests/test_objectstore.py | 97 -------
plugins/kimchi/tests/test_plugin.py | 126 ---------
plugins/kimchi/tests/test_rollbackcontext.py | 99 -------
plugins/kimchi/tests/test_server.py | 289 ---------------------
plugins/kimchi/tests/test_utils.py | 69 -----
src/wok/plugins/kimchi/tests/test_exception.py | 123 +++++++++
src/wok/plugins/kimchi/tests/test_objectstore.py | 97 +++++++
src/wok/plugins/kimchi/tests/test_plugin.py | 126 +++++++++
.../plugins/kimchi/tests/test_rollbackcontext.py | 99 +++++++
src/wok/plugins/kimchi/tests/test_server.py | 289 +++++++++++++++++++++
src/wok/plugins/kimchi/tests/test_utils.py | 69 +++++
12 files changed, 803 insertions(+), 803 deletions(-)
delete mode 100644 plugins/kimchi/tests/test_exception.py
delete mode 100644 plugins/kimchi/tests/test_objectstore.py
delete mode 100644 plugins/kimchi/tests/test_plugin.py
delete mode 100644 plugins/kimchi/tests/test_rollbackcontext.py
delete mode 100644 plugins/kimchi/tests/test_server.py
delete mode 100644 plugins/kimchi/tests/test_utils.py
create mode 100644 src/wok/plugins/kimchi/tests/test_exception.py
create mode 100644 src/wok/plugins/kimchi/tests/test_objectstore.py
create mode 100644 src/wok/plugins/kimchi/tests/test_plugin.py
create mode 100644 src/wok/plugins/kimchi/tests/test_rollbackcontext.py
create mode 100644 src/wok/plugins/kimchi/tests/test_server.py
create mode 100644 src/wok/plugins/kimchi/tests/test_utils.py
diff --git a/plugins/kimchi/tests/test_exception.py b/plugins/kimchi/tests/test_exception.py
deleted file mode 100644
index 4459aa6..0000000
--- a/plugins/kimchi/tests/test_exception.py
+++ /dev/null
@@ -1,123 +0,0 @@
-#
-# Kimchi
-#
-# Copyright IBM, Corp. 2013-2014
-#
-# 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 os
-import unittest
-
-from wok.plugins.kimchi import mockmodel
-
-from utils import get_free_port, patch_auth, request, run_server
-
-
-test_server = None
-model = None
-host = None
-port = None
-ssl_port = None
-
-
-def setup_server(environment='development'):
- global test_server, model, host, port, ssl_port
-
- patch_auth()
- model = mockmodel.MockModel('/tmp/obj-store-test')
- host = '127.0.0.1'
- port = get_free_port('http')
- ssl_port = get_free_port('https')
- test_server = run_server(host, port, ssl_port, test_mode=True, model=model,
- environment=environment)
-
-
-class ExceptionTests(unittest.TestCase):
- def tearDown(self):
- test_server.stop()
- os.unlink('/tmp/obj-store-test')
-
- def test_production_env(self):
- """
- Test reasons sanitized in production env
- """
- setup_server('production')
- # test 404
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms/blah').read()
- )
- self.assertEquals('404 Not Found', resp.get('code'))
-
- # test 405 wrong method
- resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read())
- msg = u'WOKAPI0002E: Delete is not allowed for wokroot'
- self.assertEquals('405 Method Not Allowed', resp.get('code'))
- self.assertEquals(msg, resp.get('reason'))
-
- # test 400 parse error
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms', '{', 'POST').read()
- )
- msg = u'WOKAPI0006E: Unable to parse JSON request'
- self.assertEquals('400 Bad Request', resp.get('code'))
- self.assertEquals(msg, resp.get('reason'))
- self.assertNotIn('call_stack', resp)
-
- # test 400 missing required parameter
- req = json.dumps({})
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms', req, 'POST').read()
- )
- self.assertEquals('400 Bad Request', resp.get('code'))
- m = u"KCHVM0016E: Specify a template to create a virtual machine from"
- self.assertEquals(m, resp.get('reason'))
- self.assertNotIn('call_stack', resp)
-
- def test_development_env(self):
- """
- Test traceback thrown in development env
- """
- setup_server()
- # test 404
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms/blah').read()
- )
- self.assertEquals('404 Not Found', resp.get('code'))
-
- # test 405 wrong method
- resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read())
- msg = u'WOKAPI0002E: Delete is not allowed for wokroot'
- self.assertEquals('405 Method Not Allowed', resp.get('code'))
- self.assertEquals(msg, resp.get('reason'))
-
- # test 400 parse error
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms', '{', 'POST').read()
- )
- msg = u'WOKAPI0006E: Unable to parse JSON request'
- self.assertEquals('400 Bad Request', resp.get('code'))
- self.assertEquals(msg, resp.get('reason'))
- self.assertIn('call_stack', resp)
-
- # test 400 missing required parameter
- req = json.dumps({})
- resp = json.loads(
- request(host, ssl_port, '/plugins/kimchi/vms', req, 'POST').read()
- )
- m = u"KCHVM0016E: Specify a template to create a virtual machine from"
- self.assertEquals('400 Bad Request', resp.get('code'))
- self.assertEquals(m, resp.get('reason'))
- self.assertIn('call_stack', resp)
diff --git a/plugins/kimchi/tests/test_objectstore.py b/plugins/kimchi/tests/test_objectstore.py
deleted file mode 100644
index 632786f..0000000
--- a/plugins/kimchi/tests/test_objectstore.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2015
-#
-# 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 os
-import tempfile
-import threading
-import unittest
-
-from wok import objectstore
-from wok.exception import NotFoundError
-
-
-tmpfile = None
-
-
-def setUpModule():
- global tmpfile
- tmpfile = tempfile.mktemp()
-
-
-def tearDownModule():
- os.unlink(tmpfile)
-
-
-class ObjectStoreTests(unittest.TestCase):
- def test_objectstore(self):
- store = objectstore.ObjectStore(tmpfile)
-
- with store as session:
- # Test create
- session.store('fǒǒ', 'těst1', {'α': 1})
- session.store('fǒǒ', 'těst2', {'β': 2})
-
- # Test list
- items = session.get_list('fǒǒ')
- self.assertTrue(u'těst1' in items)
- self.assertTrue(u'těst2' in items)
-
- # Test get
- item = session.get('fǒǒ', 'těst1')
- self.assertEquals(1, item[u'α'])
-
- # Test delete
- session.delete('fǒǒ', 'těst2')
- self.assertEquals(1, len(session.get_list('fǒǒ')))
-
- # Test get non-existent item
-
- self.assertRaises(NotFoundError, session.get,
- 'α', 'β')
-
- # Test delete non-existent item
- self.assertRaises(NotFoundError, session.delete,
- 'fǒǒ', 'těst2')
-
- # Test refresh existing item
- session.store('fǒǒ', 'těst1', {'α': 2})
- item = session.get('fǒǒ', 'těst1')
- self.assertEquals(2, item[u'α'])
-
- def test_object_store_threaded(self):
- def worker(ident):
- with store as session:
- session.store('foo', ident, {})
-
- store = objectstore.ObjectStore(tmpfile)
-
- threads = []
- for i in xrange(50):
- t = threading.Thread(target=worker, args=(i,))
- t.setDaemon(True)
- t.start()
- threads.append(t)
-
- for t in threads:
- t.join()
-
- with store as session:
- self.assertEquals(50, len(session.get_list('foo')))
- self.assertEquals(10, len(store._connections.keys()))
diff --git a/plugins/kimchi/tests/test_plugin.py b/plugins/kimchi/tests/test_plugin.py
deleted file mode 100644
index fc8e277..0000000
--- a/plugins/kimchi/tests/test_plugin.py
+++ /dev/null
@@ -1,126 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013-2014
-#
-# 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 os
-import unittest
-from functools import partial
-
-from wok.utils import get_enabled_plugins
-
-from wok.plugins.kimchi import mockmodel
-
-import utils
-
-
-test_server = None
-model = None
-host = None
-port = None
-ssl_port = None
-
-
-def setUpModule():
- global test_server, model, host, port, ssl_port
-
- utils.patch_auth()
- model = mockmodel.MockModel('/tmp/obj-store-test')
- host = '127.0.0.1'
- port = utils.get_free_port('http')
- ssl_port = utils.get_free_port('https')
- test_server = utils.run_server(host, port, ssl_port, test_mode=True,
- model=model)
-
-
-def tearDownModule():
- test_server.stop()
- os.unlink('/tmp/obj-store-test')
-
-
-(a)unittest.skipUnless(
- 'sample' in [plugin for plugin, _config in get_enabled_plugins()],
- 'sample plugin is not enabled, skip this test!')
-class PluginTests(unittest.TestCase):
-
- def setUp(self):
- self.request = partial(utils.request, host, ssl_port)
-
- def _create_rectangle(self, name, length, width):
- req = json.dumps({'name': name, 'length': length, 'width': width})
- resp = self.request('/plugins/sample/rectangles', req, 'POST')
- return resp
-
- def _get_rectangle(self, name):
- resp = self.request('/plugins/sample/rectangles/%s' % name)
- return json.loads(resp.read())
-
- def _create_rectangle_and_assert(self, name, length, width):
- resp = self._create_rectangle(name, length, width)
- self.assertEquals(201, resp.status)
-
- rectangle = self._get_rectangle(name)
- self.assertEquals(rectangle['name'], name)
- self.assertEquals(rectangle['length'], length)
- self.assertEquals(rectangle['width'], width)
-
- def _get_rectangles_list(self):
- resp = self.request('/plugins/sample/rectangles')
- rectangles = json.loads(resp.read())
- name_list = [rectangle['name'] for rectangle in rectangles]
- return name_list
-
- def test_rectangles(self):
- # Create two new rectangles
- self._create_rectangle_and_assert('small', 10, 8)
- self._create_rectangle_and_assert('big', 20, 16)
-
- # Verify they're in the list
- name_list = self._get_rectangles_list()
- self.assertIn('small', name_list)
- self.assertIn('big', name_list)
-
- # Update the big rectangle.
- req = json.dumps({'length': 40, 'width': 30})
- resp = self.request('/plugins/sample/rectangles/big', req, 'PUT')
- self.assertEquals(200, resp.status)
- big = self._get_rectangle('big')
- self.assertEquals(big['length'], 40)
- self.assertEquals(big['width'], 30)
-
- # Delete two rectangles
- resp = self.request('/plugins/sample/rectangles/big', '{}', 'DELETE')
- self.assertEquals(204, resp.status)
- resp = self.request('/plugins/sample/rectangles/small', '{}', 'DELETE')
- self.assertEquals(204, resp.status)
- name_list = self._get_rectangles_list()
- self.assertEquals([], name_list)
-
- def test_bad_params(self):
- # Bad name
- resp = self._create_rectangle(1.0, 30, 40)
- self.assertEquals(400, resp.status)
-
- # Bad length value
- resp = self._create_rectangle('test', -10.0, 40)
- self.assertEquals(400, resp.status)
-
- # Missing param for width
- req = json.dumps({'name': 'nowidth', 'length': 40})
- resp = self.request('/plugins/sample/rectangles', req, 'POST')
- self.assertEquals(400, resp.status)
diff --git a/plugins/kimchi/tests/test_rollbackcontext.py b/plugins/kimchi/tests/test_rollbackcontext.py
deleted file mode 100644
index 6eac6d0..0000000
--- a/plugins/kimchi/tests/test_rollbackcontext.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2014
-#
-# 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 unittest
-
-from wok.rollbackcontext import RollbackContext
-
-
-class FirstError(Exception):
- '''A hypothetical exception to be raise in the test firstly.'''
- pass
-
-
-class SecondError(Exception):
- '''A hypothetical exception to be raise in the test secondly.'''
- pass
-
-
-class RollbackContextTests(unittest.TestCase):
-
- def setUp(self):
- self._counter = 0
-
- def _inc_counter(self):
- self._counter += 1
-
- def _raise(self, exception=FirstError):
- raise exception()
-
- def test_rollback(self):
- with RollbackContext() as rollback:
- rollback.prependDefer(self._inc_counter)
- rollback.prependDefer(self._inc_counter)
- self.assertEquals(self._counter, 2)
-
- def test_raise(self):
- try:
- with RollbackContext() as rollback:
- rollback.prependDefer(self._inc_counter)
- rollback.prependDefer(self._inc_counter)
- raise FirstError()
- rollback.prependDefer(self._inc_counter)
- except FirstError:
- # All undo before the FirstError should be run
- self.assertEquals(self._counter, 2)
- else:
- self.fail('Should have raised FirstError')
-
- def test_raise_undo(self):
- try:
- with RollbackContext() as rollback:
- rollback.prependDefer(self._inc_counter)
- rollback.prependDefer(self._raise)
- rollback.prependDefer(self._inc_counter)
- except FirstError:
- # All undo should be run
- self.assertEquals(self._counter, 2)
- else:
- self.fail('Should have raised FirstError')
-
- def test_raise_prefer_original(self):
- try:
- with RollbackContext() as rollback:
- rollback.prependDefer(self._raise, SecondError)
- raise FirstError()
- except FirstError:
- pass
- except SecondError:
- self.fail('Should have preferred FirstError to SecondError')
- else:
- self.fail('Should have raised FirstError')
-
- def test_raise_prefer_first_undo(self):
- try:
- with RollbackContext() as rollback:
- rollback.prependDefer(self._raise, SecondError)
- rollback.prependDefer(self._raise, FirstError)
- except FirstError:
- pass
- except SecondError:
- self.fail('Should have preferred FirstError to SecondError')
- else:
- self.fail('Should have raised FirstError')
diff --git a/plugins/kimchi/tests/test_server.py b/plugins/kimchi/tests/test_server.py
deleted file mode 100644
index d5ef565..0000000
--- a/plugins/kimchi/tests/test_server.py
+++ /dev/null
@@ -1,289 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013-2015
-#
-# 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 base64
-import cherrypy
-import json
-import os
-import tempfile
-import threading
-import unittest
-from functools import partial
-
-from wok.control.base import Collection, Resource
-
-from wok.plugins.kimchi import mockmodel
-
-import utils
-
-
-test_server = None
-model = None
-host = None
-port = None
-ssl_port = None
-cherrypy_port = None
-tmpfile = None
-
-
-def setUpModule():
- global test_server, model, host, port, ssl_port, cherrypy_port, tmpfile
-
- utils.patch_auth()
- tmpfile = tempfile.mktemp()
- model = mockmodel.MockModel(tmpfile)
- host = '127.0.0.1'
- port = utils.get_free_port('http')
- ssl_port = utils.get_free_port('https')
- cherrypy_port = utils.get_free_port('cherrypy_port')
- test_server = utils.run_server(host, port, ssl_port, test_mode=True,
- cherrypy_port=cherrypy_port, model=model)
-
-
-def tearDownModule():
- test_server.stop()
- os.unlink(tmpfile)
-
-
-class ServerTests(unittest.TestCase):
- def setUp(self):
- self.request = partial(utils.request, host, ssl_port)
- model.reset()
-
- def assertValidJSON(self, txt):
- try:
- json.loads(txt)
- except ValueError:
- self.fail("Invalid JSON: %s" % txt)
-
- def test_server_start(self):
- """
- Test that we can start a server and receive HTTP:200.
- """
- resp = self.request('/')
- self.assertEquals(200, resp.status)
-
- def test_multithreaded_connection(self):
- def worker():
- for i in xrange(100):
- ret = model.vms_get_list()
- self.assertEquals('test', ret[0])
-
- threads = []
- for i in xrange(100):
- t = threading.Thread(target=worker)
- t.setDaemon(True)
- t.start()
- threads.append(t)
- for t in threads:
- t.join()
-
- def test_collection(self):
- c = Collection(model)
-
- # The base Collection is always empty
- cherrypy.request.method = 'GET'
- cherrypy.request.headers['Accept'] = 'application/json'
- self.assertEquals('[]', c.index())
-
- # POST and DELETE raise HTTP:405 by default
- for method in ('POST', 'DELETE'):
- cherrypy.request.method = method
- try:
- c.index()
- except cherrypy.HTTPError, e:
- self.assertEquals(405, e.code)
- else:
- self.fail("Expected exception not raised")
-
- def test_resource(self):
- r = Resource(model)
-
- # Test the base Resource representation
- cherrypy.request.method = 'GET'
- cherrypy.request.headers['Accept'] = 'application/json'
- self.assertEquals('{}', r.index())
-
- # POST and DELETE raise HTTP:405 by default
- for method in ('POST', 'DELETE'):
- cherrypy.request.method = method
- try:
- r.index()
- except cherrypy.HTTPError, e:
- self.assertEquals(405, e.code)
- else:
- self.fail("Expected exception not raised")
-
- def test_404(self):
- """
- A non-existent path should return HTTP:404
- """
- url_list = ['/plugins/kimchi/doesnotexist', '/plugins/kimchi/vms/blah']
- for url in url_list:
- resp = self.request(url)
- self.assertEquals(404, resp.status)
-
- # Verify it works for DELETE too
- resp = self.request('/plugins/kimchi/templates/blah', '', 'DELETE')
- self.assertEquals(404, resp.status)
-
- def test_accepts(self):
- """
- Verify the following expectations regarding the client Accept header:
- If omitted, default to html
- If 'application/json', serve the rest api
- If 'text/html', serve the UI
- If both of the above (in any order), serve the rest api
- If neither of the above, HTTP:406
- """
- resp = self.request("/", headers={})
- location = resp.getheader('location')
- self.assertTrue(location.endswith("login.html"))
- resp = self.request("/login.html", headers={})
- self.assertTrue('<!doctype html>' in resp.read().lower())
-
- resp = self.request("/", headers={'Accept': 'application/json'})
- self.assertValidJSON(resp.read())
-
- resp = self.request("/", headers={'Accept': 'text/html'})
- location = resp.getheader('location')
- self.assertTrue(location.endswith("login.html"))
-
- resp = self.request("/", headers={'Accept':
- 'application/json, text/html'})
- self.assertValidJSON(resp.read())
-
- resp = self.request("/", headers={'Accept':
- 'text/html, application/json'})
- self.assertValidJSON(resp.read())
-
- h = {'Accept': 'text/plain'}
- resp = self.request('/', None, 'GET', h)
- self.assertEquals(406, resp.status)
-
- def test_auth_unprotected(self):
- hdrs = {'AUTHORIZATION': ''}
- uris = ['/plugins/kimchi/js/kimchi.min.js',
- '/plugins/kimchi/css/theme-default.min.css',
- '/plugins/kimchi/images/icon-vm.png',
- '/libs/jquery-1.10.0.min.js',
- '/login.html',
- '/logout']
-
- for uri in uris:
- resp = self.request(uri, None, 'HEAD', hdrs)
- self.assertEquals(200, resp.status)
-
- def test_auth_protected(self):
- hdrs = {'AUTHORIZATION': ''}
- uris = ['/plugins/kimchi/vms',
- '/plugins/kimchi/vms/doesnotexist',
- '/tasks']
-
- for uri in uris:
- resp = self.request(uri, None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
-
- def test_auth_bad_creds(self):
- # Test HTTPBA
- hdrs = {'AUTHORIZATION': "Basic " + base64.b64encode("nouser:badpass")}
- resp = self.request('/plugins/kimchi/vms', None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
-
- # Test REST API
- hdrs = {'AUTHORIZATION': ''}
- req = json.dumps({'username': 'nouser', 'password': 'badpass'})
- resp = self.request('/login', req, 'POST', hdrs)
- self.assertEquals(401, resp.status)
-
- def test_auth_browser_no_httpba(self):
- # Kimchi detects REST requests from the browser by looking for a
- # specific header
- hdrs = {"X-Requested-With": "XMLHttpRequest"}
-
- # Try our request (Note that request() will add a valid HTTPBA header)
- resp = self.request('/plugins/kimchi/vms', None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
- self.assertEquals(None, resp.getheader('WWW-Authenticate'))
-
- def test_auth_session(self):
- hdrs = {'AUTHORIZATION': '',
- 'Content-Type': 'application/json',
- 'Accept': 'application/json'}
-
- # Test we are logged out
- resp = self.request('/tasks', None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
-
- # Execute a login call
- user, pw = mockmodel.fake_user.items()[0]
- req = json.dumps({'username': user, 'password': pw})
- resp = self.request('/login', req, 'POST', hdrs)
- self.assertEquals(200, resp.status)
-
- user_info = json.loads(resp.read())
- self.assertEquals(sorted(user_info.keys()),
- ['groups', 'roles', 'username'])
- roles = user_info['roles']
- for tab, role in roles.iteritems():
- self.assertEquals(role, u'admin')
-
- cookie = resp.getheader('set-cookie')
- hdrs['Cookie'] = cookie
-
- # Test we are logged in with the cookie
- resp = self.request('/tasks', None, 'GET', hdrs)
- self.assertEquals(200, resp.status)
-
- # Execute a logout call
- resp = self.request('/logout', '{}', 'POST', hdrs)
- self.assertEquals(200, resp.status)
- del hdrs['Cookie']
-
- # Test we are logged out
- resp = self.request('/tasks', None, 'GET', hdrs)
- self.assertEquals(401, resp.status)
-
- def test_get_param(self):
- # Create a mock ISO file
- mockiso = '/tmp/mock.iso'
- open('/tmp/mock.iso', 'w').close()
-
- # Create 2 different templates
- req = json.dumps({'name': 'test-tmpl1', 'cdrom': mockiso})
- self.request('/plugins/kimchi/templates', req, 'POST')
-
- req = json.dumps({'name': 'test-tmpl2', 'cdrom': mockiso})
- self.request('/plugins/kimchi/templates', req, 'POST')
-
- # Remove mock iso
- os.unlink(mockiso)
-
- # Get the templates
- resp = self.request('/plugins/kimchi/templates')
- self.assertEquals(200, resp.status)
- res = json.loads(resp.read())
- self.assertEquals(2, len(res))
-
- # Get a specific template
- resp = self.request('/plugins/kimchi/templates?name=test-tmpl1')
- self.assertEquals(200, resp.status)
- res = json.loads(resp.read())
- self.assertEquals(1, len(res))
- self.assertEquals('test-tmpl1', res[0]['name'])
diff --git a/plugins/kimchi/tests/test_utils.py b/plugins/kimchi/tests/test_utils.py
deleted file mode 100644
index bcb14e2..0000000
--- a/plugins/kimchi/tests/test_utils.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2015
-#
-# 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 unittest
-
-from wok.exception import InvalidParameter
-from wok.utils import convert_data_size
-
-
-class UtilsTests(unittest.TestCase):
- def test_convert_data_size(self):
- failure_data = [{'val': None, 'from': 'MiB'},
- {'val': self, 'from': 'MiB'},
- {'val': 1, 'from': None},
- {'val': 1, 'from': ''},
- {'val': 1, 'from': 'foo'},
- {'val': 1, 'from': 'kib'},
- {'val': 1, 'from': 'MiB', 'to': None},
- {'val': 1, 'from': 'MiB', 'to': ''},
- {'val': 1, 'from': 'MiB', 'to': 'foo'},
- {'val': 1, 'from': 'MiB', 'to': 'kib'}]
-
- for d in failure_data:
- if 'to' in d:
- self.assertRaises(InvalidParameter, convert_data_size,
- d['val'], d['from'], d['to'])
- else:
- self.assertRaises(InvalidParameter, convert_data_size,
- d['val'], d['from'])
-
- success_data = [{'got': convert_data_size(5, 'MiB', 'MiB'),
- 'want': 5},
- {'got': convert_data_size(5, 'MiB', 'KiB'),
- 'want': 5120},
- {'got': convert_data_size(5, 'MiB', 'M'),
- 'want': 5.24288},
- {'got': convert_data_size(5, 'MiB', 'GiB'),
- 'want': 0.0048828125},
- {'got': convert_data_size(5, 'MiB', 'Tb'),
- 'want': 4.194304e-05},
- {'got': convert_data_size(5, 'KiB', 'MiB'),
- 'want': 0.0048828125},
- {'got': convert_data_size(5, 'M', 'MiB'),
- 'want': 4.76837158203125},
- {'got': convert_data_size(5, 'GiB', 'MiB'),
- 'want': 5120},
- {'got': convert_data_size(5, 'Tb', 'MiB'),
- 'want': 596046.4477539062},
- {'got': convert_data_size(5, 'MiB'),
- 'want': convert_data_size(5, 'MiB', 'B')}]
-
- for d in success_data:
- self.assertEquals(d['got'], d['want'])
diff --git a/src/wok/plugins/kimchi/tests/test_exception.py b/src/wok/plugins/kimchi/tests/test_exception.py
new file mode 100644
index 0000000..4459aa6
--- /dev/null
+++ b/src/wok/plugins/kimchi/tests/test_exception.py
@@ -0,0 +1,123 @@
+#
+# Kimchi
+#
+# Copyright IBM, Corp. 2013-2014
+#
+# 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 os
+import unittest
+
+from wok.plugins.kimchi import mockmodel
+
+from utils import get_free_port, patch_auth, request, run_server
+
+
+test_server = None
+model = None
+host = None
+port = None
+ssl_port = None
+
+
+def setup_server(environment='development'):
+ global test_server, model, host, port, ssl_port
+
+ patch_auth()
+ model = mockmodel.MockModel('/tmp/obj-store-test')
+ host = '127.0.0.1'
+ port = get_free_port('http')
+ ssl_port = get_free_port('https')
+ test_server = run_server(host, port, ssl_port, test_mode=True, model=model,
+ environment=environment)
+
+
+class ExceptionTests(unittest.TestCase):
+ def tearDown(self):
+ test_server.stop()
+ os.unlink('/tmp/obj-store-test')
+
+ def test_production_env(self):
+ """
+ Test reasons sanitized in production env
+ """
+ setup_server('production')
+ # test 404
+ resp = json.loads(
+ request(host, ssl_port, '/plugins/kimchi/vms/blah').read()
+ )
+ self.assertEquals('404 Not Found', resp.get('code'))
+
+ # test 405 wrong method
+ resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read())
+ msg = u'WOKAPI0002E: Delete is not allowed for wokroot'
+ self.assertEquals('405 Method Not Allowed', resp.get('code'))
+ self.assertEquals(msg, resp.get('reason'))
+
+ # test 400 parse error
+ resp = json.loads(
+ request(host, ssl_port, '/plugins/kimchi/vms', '{', 'POST').read()
+ )
+ msg = u'WOKAPI0006E: Unable to parse JSON request'
+ self.assertEquals('400 Bad Request', resp.get('code'))
+ self.assertEquals(msg, resp.get('reason'))
+ self.assertNotIn('call_stack', resp)
+
+ # test 400 missing required parameter
+ req = json.dumps({})
+ resp = json.loads(
+ request(host, ssl_port, '/plugins/kimchi/vms', req, 'POST').read()
+ )
+ self.assertEquals('400 Bad Request', resp.get('code'))
+ m = u"KCHVM0016E: Specify a template to create a virtual machine from"
+ self.assertEquals(m, resp.get('reason'))
+ self.assertNotIn('call_stack', resp)
+
+ def test_development_env(self):
+ """
+ Test traceback thrown in development env
+ """
+ setup_server()
+ # test 404
+ resp = json.loads(
+ request(host, ssl_port, '/plugins/kimchi/vms/blah').read()
+ )
+ self.assertEquals('404 Not Found', resp.get('code'))
+
+ # test 405 wrong method
+ resp = json.loads(request(host, ssl_port, '/', None, 'DELETE').read())
+ msg = u'WOKAPI0002E: Delete is not allowed for wokroot'
+ self.assertEquals('405 Method Not Allowed', resp.get('code'))
+ self.assertEquals(msg, resp.get('reason'))
+
+ # test 400 parse error
+ resp = json.loads(
+ request(host, ssl_port, '/plugins/kimchi/vms', '{', 'POST').read()
+ )
+ msg = u'WOKAPI0006E: Unable to parse JSON request'
+ self.assertEquals('400 Bad Request', resp.get('code'))
+ self.assertEquals(msg, resp.get('reason'))
+ self.assertIn('call_stack', resp)
+
+ # test 400 missing required parameter
+ req = json.dumps({})
+ resp = json.loads(
+ request(host, ssl_port, '/plugins/kimchi/vms', req, 'POST').read()
+ )
+ m = u"KCHVM0016E: Specify a template to create a virtual machine from"
+ self.assertEquals('400 Bad Request', resp.get('code'))
+ self.assertEquals(m, resp.get('reason'))
+ self.assertIn('call_stack', resp)
diff --git a/src/wok/plugins/kimchi/tests/test_objectstore.py b/src/wok/plugins/kimchi/tests/test_objectstore.py
new file mode 100644
index 0000000..632786f
--- /dev/null
+++ b/src/wok/plugins/kimchi/tests/test_objectstore.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2015
+#
+# 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 os
+import tempfile
+import threading
+import unittest
+
+from wok import objectstore
+from wok.exception import NotFoundError
+
+
+tmpfile = None
+
+
+def setUpModule():
+ global tmpfile
+ tmpfile = tempfile.mktemp()
+
+
+def tearDownModule():
+ os.unlink(tmpfile)
+
+
+class ObjectStoreTests(unittest.TestCase):
+ def test_objectstore(self):
+ store = objectstore.ObjectStore(tmpfile)
+
+ with store as session:
+ # Test create
+ session.store('fǒǒ', 'těst1', {'α': 1})
+ session.store('fǒǒ', 'těst2', {'β': 2})
+
+ # Test list
+ items = session.get_list('fǒǒ')
+ self.assertTrue(u'těst1' in items)
+ self.assertTrue(u'těst2' in items)
+
+ # Test get
+ item = session.get('fǒǒ', 'těst1')
+ self.assertEquals(1, item[u'α'])
+
+ # Test delete
+ session.delete('fǒǒ', 'těst2')
+ self.assertEquals(1, len(session.get_list('fǒǒ')))
+
+ # Test get non-existent item
+
+ self.assertRaises(NotFoundError, session.get,
+ 'α', 'β')
+
+ # Test delete non-existent item
+ self.assertRaises(NotFoundError, session.delete,
+ 'fǒǒ', 'těst2')
+
+ # Test refresh existing item
+ session.store('fǒǒ', 'těst1', {'α': 2})
+ item = session.get('fǒǒ', 'těst1')
+ self.assertEquals(2, item[u'α'])
+
+ def test_object_store_threaded(self):
+ def worker(ident):
+ with store as session:
+ session.store('foo', ident, {})
+
+ store = objectstore.ObjectStore(tmpfile)
+
+ threads = []
+ for i in xrange(50):
+ t = threading.Thread(target=worker, args=(i,))
+ t.setDaemon(True)
+ t.start()
+ threads.append(t)
+
+ for t in threads:
+ t.join()
+
+ with store as session:
+ self.assertEquals(50, len(session.get_list('foo')))
+ self.assertEquals(10, len(store._connections.keys()))
diff --git a/src/wok/plugins/kimchi/tests/test_plugin.py b/src/wok/plugins/kimchi/tests/test_plugin.py
new file mode 100644
index 0000000..fc8e277
--- /dev/null
+++ b/src/wok/plugins/kimchi/tests/test_plugin.py
@@ -0,0 +1,126 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013-2014
+#
+# 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 os
+import unittest
+from functools import partial
+
+from wok.utils import get_enabled_plugins
+
+from wok.plugins.kimchi import mockmodel
+
+import utils
+
+
+test_server = None
+model = None
+host = None
+port = None
+ssl_port = None
+
+
+def setUpModule():
+ global test_server, model, host, port, ssl_port
+
+ utils.patch_auth()
+ model = mockmodel.MockModel('/tmp/obj-store-test')
+ host = '127.0.0.1'
+ port = utils.get_free_port('http')
+ ssl_port = utils.get_free_port('https')
+ test_server = utils.run_server(host, port, ssl_port, test_mode=True,
+ model=model)
+
+
+def tearDownModule():
+ test_server.stop()
+ os.unlink('/tmp/obj-store-test')
+
+
+(a)unittest.skipUnless(
+ 'sample' in [plugin for plugin, _config in get_enabled_plugins()],
+ 'sample plugin is not enabled, skip this test!')
+class PluginTests(unittest.TestCase):
+
+ def setUp(self):
+ self.request = partial(utils.request, host, ssl_port)
+
+ def _create_rectangle(self, name, length, width):
+ req = json.dumps({'name': name, 'length': length, 'width': width})
+ resp = self.request('/plugins/sample/rectangles', req, 'POST')
+ return resp
+
+ def _get_rectangle(self, name):
+ resp = self.request('/plugins/sample/rectangles/%s' % name)
+ return json.loads(resp.read())
+
+ def _create_rectangle_and_assert(self, name, length, width):
+ resp = self._create_rectangle(name, length, width)
+ self.assertEquals(201, resp.status)
+
+ rectangle = self._get_rectangle(name)
+ self.assertEquals(rectangle['name'], name)
+ self.assertEquals(rectangle['length'], length)
+ self.assertEquals(rectangle['width'], width)
+
+ def _get_rectangles_list(self):
+ resp = self.request('/plugins/sample/rectangles')
+ rectangles = json.loads(resp.read())
+ name_list = [rectangle['name'] for rectangle in rectangles]
+ return name_list
+
+ def test_rectangles(self):
+ # Create two new rectangles
+ self._create_rectangle_and_assert('small', 10, 8)
+ self._create_rectangle_and_assert('big', 20, 16)
+
+ # Verify they're in the list
+ name_list = self._get_rectangles_list()
+ self.assertIn('small', name_list)
+ self.assertIn('big', name_list)
+
+ # Update the big rectangle.
+ req = json.dumps({'length': 40, 'width': 30})
+ resp = self.request('/plugins/sample/rectangles/big', req, 'PUT')
+ self.assertEquals(200, resp.status)
+ big = self._get_rectangle('big')
+ self.assertEquals(big['length'], 40)
+ self.assertEquals(big['width'], 30)
+
+ # Delete two rectangles
+ resp = self.request('/plugins/sample/rectangles/big', '{}', 'DELETE')
+ self.assertEquals(204, resp.status)
+ resp = self.request('/plugins/sample/rectangles/small', '{}', 'DELETE')
+ self.assertEquals(204, resp.status)
+ name_list = self._get_rectangles_list()
+ self.assertEquals([], name_list)
+
+ def test_bad_params(self):
+ # Bad name
+ resp = self._create_rectangle(1.0, 30, 40)
+ self.assertEquals(400, resp.status)
+
+ # Bad length value
+ resp = self._create_rectangle('test', -10.0, 40)
+ self.assertEquals(400, resp.status)
+
+ # Missing param for width
+ req = json.dumps({'name': 'nowidth', 'length': 40})
+ resp = self.request('/plugins/sample/rectangles', req, 'POST')
+ self.assertEquals(400, resp.status)
diff --git a/src/wok/plugins/kimchi/tests/test_rollbackcontext.py b/src/wok/plugins/kimchi/tests/test_rollbackcontext.py
new file mode 100644
index 0000000..6eac6d0
--- /dev/null
+++ b/src/wok/plugins/kimchi/tests/test_rollbackcontext.py
@@ -0,0 +1,99 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2014
+#
+# 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 unittest
+
+from wok.rollbackcontext import RollbackContext
+
+
+class FirstError(Exception):
+ '''A hypothetical exception to be raise in the test firstly.'''
+ pass
+
+
+class SecondError(Exception):
+ '''A hypothetical exception to be raise in the test secondly.'''
+ pass
+
+
+class RollbackContextTests(unittest.TestCase):
+
+ def setUp(self):
+ self._counter = 0
+
+ def _inc_counter(self):
+ self._counter += 1
+
+ def _raise(self, exception=FirstError):
+ raise exception()
+
+ def test_rollback(self):
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._inc_counter)
+ rollback.prependDefer(self._inc_counter)
+ self.assertEquals(self._counter, 2)
+
+ def test_raise(self):
+ try:
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._inc_counter)
+ rollback.prependDefer(self._inc_counter)
+ raise FirstError()
+ rollback.prependDefer(self._inc_counter)
+ except FirstError:
+ # All undo before the FirstError should be run
+ self.assertEquals(self._counter, 2)
+ else:
+ self.fail('Should have raised FirstError')
+
+ def test_raise_undo(self):
+ try:
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._inc_counter)
+ rollback.prependDefer(self._raise)
+ rollback.prependDefer(self._inc_counter)
+ except FirstError:
+ # All undo should be run
+ self.assertEquals(self._counter, 2)
+ else:
+ self.fail('Should have raised FirstError')
+
+ def test_raise_prefer_original(self):
+ try:
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._raise, SecondError)
+ raise FirstError()
+ except FirstError:
+ pass
+ except SecondError:
+ self.fail('Should have preferred FirstError to SecondError')
+ else:
+ self.fail('Should have raised FirstError')
+
+ def test_raise_prefer_first_undo(self):
+ try:
+ with RollbackContext() as rollback:
+ rollback.prependDefer(self._raise, SecondError)
+ rollback.prependDefer(self._raise, FirstError)
+ except FirstError:
+ pass
+ except SecondError:
+ self.fail('Should have preferred FirstError to SecondError')
+ else:
+ self.fail('Should have raised FirstError')
diff --git a/src/wok/plugins/kimchi/tests/test_server.py b/src/wok/plugins/kimchi/tests/test_server.py
new file mode 100644
index 0000000..d5ef565
--- /dev/null
+++ b/src/wok/plugins/kimchi/tests/test_server.py
@@ -0,0 +1,289 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013-2015
+#
+# 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 base64
+import cherrypy
+import json
+import os
+import tempfile
+import threading
+import unittest
+from functools import partial
+
+from wok.control.base import Collection, Resource
+
+from wok.plugins.kimchi import mockmodel
+
+import utils
+
+
+test_server = None
+model = None
+host = None
+port = None
+ssl_port = None
+cherrypy_port = None
+tmpfile = None
+
+
+def setUpModule():
+ global test_server, model, host, port, ssl_port, cherrypy_port, tmpfile
+
+ utils.patch_auth()
+ tmpfile = tempfile.mktemp()
+ model = mockmodel.MockModel(tmpfile)
+ host = '127.0.0.1'
+ port = utils.get_free_port('http')
+ ssl_port = utils.get_free_port('https')
+ cherrypy_port = utils.get_free_port('cherrypy_port')
+ test_server = utils.run_server(host, port, ssl_port, test_mode=True,
+ cherrypy_port=cherrypy_port, model=model)
+
+
+def tearDownModule():
+ test_server.stop()
+ os.unlink(tmpfile)
+
+
+class ServerTests(unittest.TestCase):
+ def setUp(self):
+ self.request = partial(utils.request, host, ssl_port)
+ model.reset()
+
+ def assertValidJSON(self, txt):
+ try:
+ json.loads(txt)
+ except ValueError:
+ self.fail("Invalid JSON: %s" % txt)
+
+ def test_server_start(self):
+ """
+ Test that we can start a server and receive HTTP:200.
+ """
+ resp = self.request('/')
+ self.assertEquals(200, resp.status)
+
+ def test_multithreaded_connection(self):
+ def worker():
+ for i in xrange(100):
+ ret = model.vms_get_list()
+ self.assertEquals('test', ret[0])
+
+ threads = []
+ for i in xrange(100):
+ t = threading.Thread(target=worker)
+ t.setDaemon(True)
+ t.start()
+ threads.append(t)
+ for t in threads:
+ t.join()
+
+ def test_collection(self):
+ c = Collection(model)
+
+ # The base Collection is always empty
+ cherrypy.request.method = 'GET'
+ cherrypy.request.headers['Accept'] = 'application/json'
+ self.assertEquals('[]', c.index())
+
+ # POST and DELETE raise HTTP:405 by default
+ for method in ('POST', 'DELETE'):
+ cherrypy.request.method = method
+ try:
+ c.index()
+ except cherrypy.HTTPError, e:
+ self.assertEquals(405, e.code)
+ else:
+ self.fail("Expected exception not raised")
+
+ def test_resource(self):
+ r = Resource(model)
+
+ # Test the base Resource representation
+ cherrypy.request.method = 'GET'
+ cherrypy.request.headers['Accept'] = 'application/json'
+ self.assertEquals('{}', r.index())
+
+ # POST and DELETE raise HTTP:405 by default
+ for method in ('POST', 'DELETE'):
+ cherrypy.request.method = method
+ try:
+ r.index()
+ except cherrypy.HTTPError, e:
+ self.assertEquals(405, e.code)
+ else:
+ self.fail("Expected exception not raised")
+
+ def test_404(self):
+ """
+ A non-existent path should return HTTP:404
+ """
+ url_list = ['/plugins/kimchi/doesnotexist', '/plugins/kimchi/vms/blah']
+ for url in url_list:
+ resp = self.request(url)
+ self.assertEquals(404, resp.status)
+
+ # Verify it works for DELETE too
+ resp = self.request('/plugins/kimchi/templates/blah', '', 'DELETE')
+ self.assertEquals(404, resp.status)
+
+ def test_accepts(self):
+ """
+ Verify the following expectations regarding the client Accept header:
+ If omitted, default to html
+ If 'application/json', serve the rest api
+ If 'text/html', serve the UI
+ If both of the above (in any order), serve the rest api
+ If neither of the above, HTTP:406
+ """
+ resp = self.request("/", headers={})
+ location = resp.getheader('location')
+ self.assertTrue(location.endswith("login.html"))
+ resp = self.request("/login.html", headers={})
+ self.assertTrue('<!doctype html>' in resp.read().lower())
+
+ resp = self.request("/", headers={'Accept': 'application/json'})
+ self.assertValidJSON(resp.read())
+
+ resp = self.request("/", headers={'Accept': 'text/html'})
+ location = resp.getheader('location')
+ self.assertTrue(location.endswith("login.html"))
+
+ resp = self.request("/", headers={'Accept':
+ 'application/json, text/html'})
+ self.assertValidJSON(resp.read())
+
+ resp = self.request("/", headers={'Accept':
+ 'text/html, application/json'})
+ self.assertValidJSON(resp.read())
+
+ h = {'Accept': 'text/plain'}
+ resp = self.request('/', None, 'GET', h)
+ self.assertEquals(406, resp.status)
+
+ def test_auth_unprotected(self):
+ hdrs = {'AUTHORIZATION': ''}
+ uris = ['/plugins/kimchi/js/kimchi.min.js',
+ '/plugins/kimchi/css/theme-default.min.css',
+ '/plugins/kimchi/images/icon-vm.png',
+ '/libs/jquery-1.10.0.min.js',
+ '/login.html',
+ '/logout']
+
+ for uri in uris:
+ resp = self.request(uri, None, 'HEAD', hdrs)
+ self.assertEquals(200, resp.status)
+
+ def test_auth_protected(self):
+ hdrs = {'AUTHORIZATION': ''}
+ uris = ['/plugins/kimchi/vms',
+ '/plugins/kimchi/vms/doesnotexist',
+ '/tasks']
+
+ for uri in uris:
+ resp = self.request(uri, None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+
+ def test_auth_bad_creds(self):
+ # Test HTTPBA
+ hdrs = {'AUTHORIZATION': "Basic " + base64.b64encode("nouser:badpass")}
+ resp = self.request('/plugins/kimchi/vms', None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+
+ # Test REST API
+ hdrs = {'AUTHORIZATION': ''}
+ req = json.dumps({'username': 'nouser', 'password': 'badpass'})
+ resp = self.request('/login', req, 'POST', hdrs)
+ self.assertEquals(401, resp.status)
+
+ def test_auth_browser_no_httpba(self):
+ # Kimchi detects REST requests from the browser by looking for a
+ # specific header
+ hdrs = {"X-Requested-With": "XMLHttpRequest"}
+
+ # Try our request (Note that request() will add a valid HTTPBA header)
+ resp = self.request('/plugins/kimchi/vms', None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+ self.assertEquals(None, resp.getheader('WWW-Authenticate'))
+
+ def test_auth_session(self):
+ hdrs = {'AUTHORIZATION': '',
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'}
+
+ # Test we are logged out
+ resp = self.request('/tasks', None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+
+ # Execute a login call
+ user, pw = mockmodel.fake_user.items()[0]
+ req = json.dumps({'username': user, 'password': pw})
+ resp = self.request('/login', req, 'POST', hdrs)
+ self.assertEquals(200, resp.status)
+
+ user_info = json.loads(resp.read())
+ self.assertEquals(sorted(user_info.keys()),
+ ['groups', 'roles', 'username'])
+ roles = user_info['roles']
+ for tab, role in roles.iteritems():
+ self.assertEquals(role, u'admin')
+
+ cookie = resp.getheader('set-cookie')
+ hdrs['Cookie'] = cookie
+
+ # Test we are logged in with the cookie
+ resp = self.request('/tasks', None, 'GET', hdrs)
+ self.assertEquals(200, resp.status)
+
+ # Execute a logout call
+ resp = self.request('/logout', '{}', 'POST', hdrs)
+ self.assertEquals(200, resp.status)
+ del hdrs['Cookie']
+
+ # Test we are logged out
+ resp = self.request('/tasks', None, 'GET', hdrs)
+ self.assertEquals(401, resp.status)
+
+ def test_get_param(self):
+ # Create a mock ISO file
+ mockiso = '/tmp/mock.iso'
+ open('/tmp/mock.iso', 'w').close()
+
+ # Create 2 different templates
+ req = json.dumps({'name': 'test-tmpl1', 'cdrom': mockiso})
+ self.request('/plugins/kimchi/templates', req, 'POST')
+
+ req = json.dumps({'name': 'test-tmpl2', 'cdrom': mockiso})
+ self.request('/plugins/kimchi/templates', req, 'POST')
+
+ # Remove mock iso
+ os.unlink(mockiso)
+
+ # Get the templates
+ resp = self.request('/plugins/kimchi/templates')
+ self.assertEquals(200, resp.status)
+ res = json.loads(resp.read())
+ self.assertEquals(2, len(res))
+
+ # Get a specific template
+ resp = self.request('/plugins/kimchi/templates?name=test-tmpl1')
+ self.assertEquals(200, resp.status)
+ res = json.loads(resp.read())
+ self.assertEquals(1, len(res))
+ self.assertEquals('test-tmpl1', res[0]['name'])
diff --git a/src/wok/plugins/kimchi/tests/test_utils.py b/src/wok/plugins/kimchi/tests/test_utils.py
new file mode 100644
index 0000000..bcb14e2
--- /dev/null
+++ b/src/wok/plugins/kimchi/tests/test_utils.py
@@ -0,0 +1,69 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2015
+#
+# 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 unittest
+
+from wok.exception import InvalidParameter
+from wok.utils import convert_data_size
+
+
+class UtilsTests(unittest.TestCase):
+ def test_convert_data_size(self):
+ failure_data = [{'val': None, 'from': 'MiB'},
+ {'val': self, 'from': 'MiB'},
+ {'val': 1, 'from': None},
+ {'val': 1, 'from': ''},
+ {'val': 1, 'from': 'foo'},
+ {'val': 1, 'from': 'kib'},
+ {'val': 1, 'from': 'MiB', 'to': None},
+ {'val': 1, 'from': 'MiB', 'to': ''},
+ {'val': 1, 'from': 'MiB', 'to': 'foo'},
+ {'val': 1, 'from': 'MiB', 'to': 'kib'}]
+
+ for d in failure_data:
+ if 'to' in d:
+ self.assertRaises(InvalidParameter, convert_data_size,
+ d['val'], d['from'], d['to'])
+ else:
+ self.assertRaises(InvalidParameter, convert_data_size,
+ d['val'], d['from'])
+
+ success_data = [{'got': convert_data_size(5, 'MiB', 'MiB'),
+ 'want': 5},
+ {'got': convert_data_size(5, 'MiB', 'KiB'),
+ 'want': 5120},
+ {'got': convert_data_size(5, 'MiB', 'M'),
+ 'want': 5.24288},
+ {'got': convert_data_size(5, 'MiB', 'GiB'),
+ 'want': 0.0048828125},
+ {'got': convert_data_size(5, 'MiB', 'Tb'),
+ 'want': 4.194304e-05},
+ {'got': convert_data_size(5, 'KiB', 'MiB'),
+ 'want': 0.0048828125},
+ {'got': convert_data_size(5, 'M', 'MiB'),
+ 'want': 4.76837158203125},
+ {'got': convert_data_size(5, 'GiB', 'MiB'),
+ 'want': 5120},
+ {'got': convert_data_size(5, 'Tb', 'MiB'),
+ 'want': 596046.4477539062},
+ {'got': convert_data_size(5, 'MiB'),
+ 'want': convert_data_size(5, 'MiB', 'B')}]
+
+ for d in success_data:
+ self.assertEquals(d['got'], d['want'])
--
2.4.3
9 years, 2 months
[PATCH 0/5 V2] [WOK] Set new structure to Wok plugins.
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)linux.vnet.ibm.com>
This patch-set moves the plugins structure to a new path inside src/wok, and fix
and modify all necessary build, config and code files to use the new structure.
In addition, fix all bugs related to build and install Wok and Kimchi in the
system and generate the necessary RPM and DEB files.
Paulo Vital (5):
Move plugins directory to src/wok structure.
Change relative to absolute imports in Kimchi.
Update build and config files to new plugins path.
Make dynamic the state_dir path in Kimchi.
Adding COPYING and CONTRIBUTE files into Kimchi.
Makefile.am | 20 +-
configure.ac | 16 +-
plugins/Makefile.am | 25 -
plugins/__init__.py | 18 -
plugins/kimchi/.gitignore | 37 -
plugins/kimchi/API.json | 836 -------
plugins/kimchi/INSTALL | 369 ---
plugins/kimchi/Makefile.am | 161 --
plugins/kimchi/README.md | 1 -
plugins/kimchi/VERSION | 1 -
plugins/kimchi/__init__.py | 21 -
plugins/kimchi/autogen.sh | 21 -
plugins/kimchi/build-aux/config.rpath | 672 ------
plugins/kimchi/build-aux/genChangelog | 25 -
plugins/kimchi/build-aux/pkg-version | 59 -
plugins/kimchi/config.py.in | 144 --
plugins/kimchi/config.rpath | 672 ------
plugins/kimchi/configure.ac | 119 -
plugins/kimchi/contrib/DEBIAN/Makefile.am | 17 -
plugins/kimchi/contrib/DEBIAN/control.in | 30 -
plugins/kimchi/contrib/Makefile.am | 34 -
plugins/kimchi/contrib/check_i18n.py | 82 -
plugins/kimchi/contrib/kimchi.spec.fedora.in | 119 -
plugins/kimchi/contrib/kimchi.spec.suse.in | 107 -
plugins/kimchi/contrib/make-deb.sh.in | 15 -
plugins/kimchi/control/Makefile.am | 27 -
plugins/kimchi/control/__init__.py | 26 -
plugins/kimchi/control/config.py | 57 -
plugins/kimchi/control/cpuinfo.py | 37 -
plugins/kimchi/control/debugreports.py | 61 -
plugins/kimchi/control/groups.py | 28 -
plugins/kimchi/control/host.py | 157 --
plugins/kimchi/control/interfaces.py | 46 -
plugins/kimchi/control/networks.py | 54 -
plugins/kimchi/control/peers.py | 29 -
plugins/kimchi/control/storagepools.py | 116 -
plugins/kimchi/control/storageservers.py | 60 -
plugins/kimchi/control/storagevolumes.py | 83 -
plugins/kimchi/control/templates.py | 58 -
plugins/kimchi/control/users.py | 35 -
plugins/kimchi/control/vm/Makefile.am | 26 -
plugins/kimchi/control/vm/__init__.py | 26 -
plugins/kimchi/control/vm/hostdevs.py | 43 -
plugins/kimchi/control/vm/ifaces.py | 45 -
plugins/kimchi/control/vm/snapshots.py | 58 -
plugins/kimchi/control/vm/storages.py | 45 -
plugins/kimchi/control/vms.py | 67 -
plugins/kimchi/disks.py | 196 --
plugins/kimchi/distroloader.py | 67 -
plugins/kimchi/distros.d/Makefile.am | 22 -
plugins/kimchi/distros.d/debian.json | 9 -
plugins/kimchi/distros.d/fedora.json | 30 -
plugins/kimchi/distros.d/gentoo.json | 9 -
plugins/kimchi/distros.d/opensuse.json | 23 -
plugins/kimchi/distros.d/ubuntu.json | 37 -
plugins/kimchi/docs/API.md | 1116 ---------
plugins/kimchi/docs/Makefile.am | 28 -
plugins/kimchi/docs/README-federation.md | 60 -
plugins/kimchi/docs/README.md | 247 --
plugins/kimchi/docs/kimchi-guest.png | Bin 192281 -> 0 bytes
plugins/kimchi/docs/kimchi-login.png | Bin 318041 -> 0 bytes
plugins/kimchi/docs/kimchi-templates.png | Bin 329678 -> 0 bytes
plugins/kimchi/i18n.py | 335 ---
plugins/kimchi/imageinfo.py | 72 -
plugins/kimchi/iscsi.py | 88 -
plugins/kimchi/isoinfo.py | 506 -----
plugins/kimchi/kimchi.conf | 37 -
plugins/kimchi/kvmusertests.py | 79 -
plugins/kimchi/m4/ac_python_module.m4 | 30 -
plugins/kimchi/m4/gettext.m4 | 383 ----
plugins/kimchi/m4/iconv.m4 | 214 --
plugins/kimchi/m4/intlmacosx.m4 | 51 -
plugins/kimchi/m4/lib-ld.m4 | 110 -
plugins/kimchi/m4/lib-link.m4 | 774 -------
plugins/kimchi/m4/lib-prefix.m4 | 224 --
plugins/kimchi/m4/nls.m4 | 32 -
plugins/kimchi/m4/po.m4 | 449 ----
plugins/kimchi/m4/progtest.m4 | 92 -
plugins/kimchi/mockmodel.py | 627 ------
plugins/kimchi/model/Makefile.am | 25 -
plugins/kimchi/model/__init__.py | 18 -
plugins/kimchi/model/config.py | 176 --
plugins/kimchi/model/cpuinfo.py | 126 --
plugins/kimchi/model/debugreports.py | 213 --
plugins/kimchi/model/diskutils.py | 75 -
plugins/kimchi/model/featuretests.py | 259 ---
plugins/kimchi/model/groups.py | 67 -
plugins/kimchi/model/host.py | 476 ----
plugins/kimchi/model/hostdev.py | 324 ---
plugins/kimchi/model/interfaces.py | 44 -
plugins/kimchi/model/libvirtconnection.py | 136 --
plugins/kimchi/model/libvirtstoragepool.py | 264 ---
plugins/kimchi/model/model.py | 52 -
plugins/kimchi/model/networks.py | 382 ----
plugins/kimchi/model/peers.py | 72 -
plugins/kimchi/model/storagepools.py | 490 ----
plugins/kimchi/model/storageservers.py | 81 -
plugins/kimchi/model/storagetargets.py | 122 -
plugins/kimchi/model/storagevolumes.py | 542 -----
plugins/kimchi/model/templates.py | 303 ---
plugins/kimchi/model/users.py | 90 -
plugins/kimchi/model/utils.py | 161 --
plugins/kimchi/model/vmhostdevs.py | 336 ---
plugins/kimchi/model/vmifaces.py | 186 --
plugins/kimchi/model/vms.py | 1307 -----------
plugins/kimchi/model/vmsnapshots.py | 204 --
plugins/kimchi/model/vmstorages.py | 252 ---
plugins/kimchi/netinfo.py | 216 --
plugins/kimchi/network.py | 62 -
plugins/kimchi/osinfo.py | 213 --
plugins/kimchi/po/LINGUAS | 11 -
plugins/kimchi/po/Makefile.in.in | 398 ----
plugins/kimchi/po/Makevars | 41 -
plugins/kimchi/po/POTFILES.in | 3 -
plugins/kimchi/po/de_DE.po | 2288 -------------------
plugins/kimchi/po/en_US.po | 2075 -----------------
plugins/kimchi/po/es_ES.po | 2305 -------------------
plugins/kimchi/po/fr_FR.po | 2338 -------------------
plugins/kimchi/po/gen-pot.in | 9 -
plugins/kimchi/po/it_IT.po | 2274 -------------------
plugins/kimchi/po/ja_JP.po | 2269 -------------------
plugins/kimchi/po/kimchi.pot | 2074 -----------------
plugins/kimchi/po/ko_KR.po | 2197 ------------------
plugins/kimchi/po/pt_BR.po | 2369 --------------------
plugins/kimchi/po/ru_RU.po | 2198 ------------------
plugins/kimchi/po/zh_CN.po | 2186 ------------------
plugins/kimchi/po/zh_TW.po | 2138 ------------------
plugins/kimchi/repositories.py | 529 -----
plugins/kimchi/root.py | 69 -
plugins/kimchi/scan.py | 89 -
plugins/kimchi/screenshot.py | 184 --
plugins/kimchi/swupdate.py | 263 ---
plugins/kimchi/template.conf | 47 -
plugins/kimchi/tests/Makefile.am | 50 -
plugins/kimchi/tests/iso_gen.py | 212 --
plugins/kimchi/tests/run_tests.sh.in | 55 -
plugins/kimchi/tests/test_authorization.py | 178 --
plugins/kimchi/tests/test_config.py.in | 267 ---
plugins/kimchi/tests/test_host.py | 206 --
plugins/kimchi/tests/test_mock_network.py | 73 -
plugins/kimchi/tests/test_mock_storagepool.py | 147 --
plugins/kimchi/tests/test_mock_storagevolume.py | 98 -
plugins/kimchi/tests/test_mockmodel.py | 141 --
plugins/kimchi/tests/test_model.py | 1248 -----------
plugins/kimchi/tests/test_model_network.py | 149 --
plugins/kimchi/tests/test_model_storagepool.py | 123 -
plugins/kimchi/tests/test_model_storagevolume.py | 280 ---
plugins/kimchi/tests/test_networkxml.py | 172 --
plugins/kimchi/tests/test_osinfo.py | 69 -
plugins/kimchi/tests/test_rest.py | 1327 -----------
plugins/kimchi/tests/test_storagepoolxml.py | 171 --
plugins/kimchi/tests/test_template.py | 387 ----
plugins/kimchi/tests/test_vmtemplate.py | 116 -
plugins/kimchi/tests/test_yumparser.py | 162 --
plugins/kimchi/tests/utils.py | 260 ---
plugins/kimchi/ui/Makefile.am | 20 -
plugins/kimchi/ui/config/Makefile.am | 22 -
plugins/kimchi/ui/config/tab-ext.xml | 38 -
plugins/kimchi/ui/css/Makefile.am | 26 -
plugins/kimchi/ui/css/theme-default/guest-edit.css | 424 ----
.../ui/css/theme-default/guest-storage-add.css | 81 -
plugins/kimchi/ui/css/theme-default/host.css | 287 ---
plugins/kimchi/ui/css/theme-default/icon.css | 106 -
plugins/kimchi/ui/css/theme-default/list.css | 326 ---
plugins/kimchi/ui/css/theme-default/network.css | 267 ---
plugins/kimchi/ui/css/theme-default/report-add.css | 37 -
.../kimchi/ui/css/theme-default/report-rename.css | 39 -
.../kimchi/ui/css/theme-default/repository-add.css | 42 -
.../ui/css/theme-default/repository-edit.css | 88 -
plugins/kimchi/ui/css/theme-default/storage.css | 550 -----
.../css/theme-default/storagepool-add-volume.css | 36 -
.../kimchi/ui/css/theme-default/template-edit.css | 175 --
plugins/kimchi/ui/css/theme-default/template.css | 85 -
.../kimchi/ui/css/theme-default/template_add.css | 317 ---
.../kimchi/ui/css/theme-default/template_list.css | 267 ---
plugins/kimchi/ui/images/Makefile.am | 22 -
plugins/kimchi/ui/images/icon-centos.png | Bin 4734 -> 0 bytes
plugins/kimchi/ui/images/icon-debian.png | Bin 4239 -> 0 bytes
plugins/kimchi/ui/images/icon-fedora.png | Bin 4449 -> 0 bytes
plugins/kimchi/ui/images/icon-gentoo.png | Bin 15307 -> 0 bytes
plugins/kimchi/ui/images/icon-opensuse.png | Bin 3046 -> 0 bytes
plugins/kimchi/ui/images/icon-ubuntu.png | Bin 4818 -> 0 bytes
plugins/kimchi/ui/images/icon-vm.png | Bin 2976 -> 0 bytes
plugins/kimchi/ui/images/theme-default/Makefile.am | 20 -
.../kimchi/ui/images/theme-default/ac22_pause.png | Bin 1219 -> 0 bytes
.../ui/images/theme-default/ac22_pause_grey.png | Bin 1175 -> 0 bytes
.../kimchi/ui/images/theme-default/ac24_resume.png | Bin 1341 -> 0 bytes
.../ui/images/theme-default/ac24_resume_grey.png | Bin 1282 -> 0 bytes
.../ui/images/theme-default/arrow-down-black.png | Bin 2942 -> 0 bytes
.../ui/images/theme-default/arrow-down-disable.png | Bin 472 -> 0 bytes
.../kimchi/ui/images/theme-default/arrow-down.png | Bin 537 -> 0 bytes
.../kimchi/ui/images/theme-default/arrow-up.png | Bin 510 -> 0 bytes
.../kimchi/ui/images/theme-default/arrow_out.png | Bin 3048 -> 0 bytes
plugins/kimchi/ui/images/theme-default/group.png | Bin 1703 -> 0 bytes
.../ui/images/theme-default/host-icon-sprite.png | Bin 1034 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-back.png | Bin 244 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-camera.png | Bin 4860 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-design.png | Bin 4562 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-detail.png | Bin 3079 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-iso.png | Bin 4188 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-list.png | Bin 2983 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-load.png | Bin 3678 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-local.png | Bin 425 -> 0 bytes
.../ui/images/theme-default/icon-power-down.png | Bin 4372 -> 0 bytes
.../ui/images/theme-default/icon-power-up.png | Bin 4367 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-qcow2.png | Bin 4684 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-raw.png | Bin 4679 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-remote.png | Bin 1005 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-reset.png | Bin 4576 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-search.png | Bin 4197 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-sort.png | Bin 3421 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-tree.png | Bin 3526 -> 0 bytes
.../kimchi/ui/images/theme-default/icon-user.png | Bin 5366 -> 0 bytes
.../images/theme-default/icon-volume-default.png | Bin 4265 -> 0 bytes
.../images/theme-default/kimchi-loading15x15.gif | Bin 1653 -> 0 bytes
plugins/kimchi/ui/images/theme-default/loading.gif | Bin 2190 -> 0 bytes
plugins/kimchi/ui/images/theme-default/user.png | Bin 1322 -> 0 bytes
plugins/kimchi/ui/js/Makefile.am | 27 -
plugins/kimchi/ui/js/src/kimchi.api.js | 1355 -----------
plugins/kimchi/ui/js/src/kimchi.guest_add_main.js | 86 -
plugins/kimchi/ui/js/src/kimchi.guest_edit_main.js | 759 -------
plugins/kimchi/ui/js/src/kimchi.guest_main.js | 511 -----
.../kimchi/ui/js/src/kimchi.guest_media_main.js | 56 -
.../ui/js/src/kimchi.guest_storage_add.main.js | 199 --
plugins/kimchi/ui/js/src/kimchi.host.js | 858 -------
plugins/kimchi/ui/js/src/kimchi.main.js | 26 -
plugins/kimchi/ui/js/src/kimchi.network.js | 442 ----
plugins/kimchi/ui/js/src/kimchi.report_add_main.js | 72 -
.../kimchi/ui/js/src/kimchi.report_rename_main.js | 66 -
.../kimchi/ui/js/src/kimchi.repository_add_main.js | 96 -
.../ui/js/src/kimchi.repository_edit_main.js | 74 -
plugins/kimchi/ui/js/src/kimchi.storage_main.js | 428 ----
.../ui/js/src/kimchi.storagepool_add_main.js | 414 ----
.../js/src/kimchi.storagepool_add_volume_main.js | 179 --
.../kimchi/ui/js/src/kimchi.template_add_main.js | 441 ----
.../kimchi/ui/js/src/kimchi.template_edit_main.js | 343 ---
plugins/kimchi/ui/js/src/kimchi.template_main.js | 111 -
plugins/kimchi/ui/pages/Makefile.am | 22 -
plugins/kimchi/ui/pages/guest-add.html.tmpl | 98 -
plugins/kimchi/ui/pages/guest-edit.html.tmpl | 311 ---
.../kimchi/ui/pages/guest-storage-add.html.tmpl | 103 -
plugins/kimchi/ui/pages/guest.html.tmpl | 77 -
plugins/kimchi/ui/pages/guests.html.tmpl | 65 -
plugins/kimchi/ui/pages/help/Makefile.am | 34 -
plugins/kimchi/ui/pages/help/de_DE/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/de_DE/guests.dita | 127 --
plugins/kimchi/ui/pages/help/de_DE/host.dita | 49 -
plugins/kimchi/ui/pages/help/de_DE/network.dita | 62 -
plugins/kimchi/ui/pages/help/de_DE/storage.dita | 86 -
plugins/kimchi/ui/pages/help/de_DE/templates.dita | 112 -
plugins/kimchi/ui/pages/help/dita-help.xsl | 26 -
plugins/kimchi/ui/pages/help/en_US/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/en_US/guests.dita | 136 --
plugins/kimchi/ui/pages/help/en_US/host.dita | 70 -
plugins/kimchi/ui/pages/help/en_US/network.dita | 68 -
plugins/kimchi/ui/pages/help/en_US/storage.dita | 99 -
plugins/kimchi/ui/pages/help/en_US/templates.dita | 123 -
plugins/kimchi/ui/pages/help/es_ES/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/es_ES/guests.dita | 120 -
plugins/kimchi/ui/pages/help/es_ES/host.dita | 49 -
plugins/kimchi/ui/pages/help/es_ES/network.dita | 61 -
plugins/kimchi/ui/pages/help/es_ES/storage.dita | 86 -
plugins/kimchi/ui/pages/help/es_ES/templates.dita | 111 -
plugins/kimchi/ui/pages/help/fr_FR/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/fr_FR/guests.dita | 130 --
plugins/kimchi/ui/pages/help/fr_FR/host.dita | 68 -
plugins/kimchi/ui/pages/help/fr_FR/network.dita | 67 -
plugins/kimchi/ui/pages/help/fr_FR/storage.dita | 93 -
plugins/kimchi/ui/pages/help/fr_FR/templates.dita | 120 -
plugins/kimchi/ui/pages/help/it_IT/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/it_IT/guests.dita | 123 -
plugins/kimchi/ui/pages/help/it_IT/host.dita | 51 -
plugins/kimchi/ui/pages/help/it_IT/network.dita | 63 -
plugins/kimchi/ui/pages/help/it_IT/storage.dita | 91 -
plugins/kimchi/ui/pages/help/it_IT/templates.dita | 115 -
plugins/kimchi/ui/pages/help/ja_JP/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/ja_JP/guests.dita | 172 --
plugins/kimchi/ui/pages/help/ja_JP/host.dita | 70 -
plugins/kimchi/ui/pages/help/ja_JP/network.dita | 83 -
plugins/kimchi/ui/pages/help/ja_JP/storage.dita | 120 -
plugins/kimchi/ui/pages/help/ja_JP/templates.dita | 150 --
plugins/kimchi/ui/pages/help/kimchi.css | 208 --
plugins/kimchi/ui/pages/help/ko_KR/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/ko_KR/guests.dita | 119 -
plugins/kimchi/ui/pages/help/ko_KR/host.dita | 47 -
plugins/kimchi/ui/pages/help/ko_KR/network.dita | 61 -
plugins/kimchi/ui/pages/help/ko_KR/storage.dita | 86 -
plugins/kimchi/ui/pages/help/ko_KR/templates.dita | 111 -
plugins/kimchi/ui/pages/help/pt_BR/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/pt_BR/guests.dita | 137 --
plugins/kimchi/ui/pages/help/pt_BR/host.dita | 74 -
plugins/kimchi/ui/pages/help/pt_BR/network.dita | 72 -
plugins/kimchi/ui/pages/help/pt_BR/storage.dita | 102 -
plugins/kimchi/ui/pages/help/pt_BR/templates.dita | 127 --
plugins/kimchi/ui/pages/help/ru_RU/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/ru_RU/guests.dita | 122 -
plugins/kimchi/ui/pages/help/ru_RU/host.dita | 48 -
plugins/kimchi/ui/pages/help/ru_RU/network.dita | 61 -
plugins/kimchi/ui/pages/help/ru_RU/storage.dita | 88 -
plugins/kimchi/ui/pages/help/ru_RU/templates.dita | 111 -
plugins/kimchi/ui/pages/help/zh_CN/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/zh_CN/guests.dita | 118 -
plugins/kimchi/ui/pages/help/zh_CN/host.dita | 45 -
plugins/kimchi/ui/pages/help/zh_CN/network.dita | 61 -
plugins/kimchi/ui/pages/help/zh_CN/storage.dita | 84 -
plugins/kimchi/ui/pages/help/zh_CN/templates.dita | 111 -
plugins/kimchi/ui/pages/help/zh_TW/Makefile.am | 23 -
plugins/kimchi/ui/pages/help/zh_TW/guests.dita | 120 -
plugins/kimchi/ui/pages/help/zh_TW/host.dita | 50 -
plugins/kimchi/ui/pages/help/zh_TW/network.dita | 61 -
plugins/kimchi/ui/pages/help/zh_TW/storage.dita | 88 -
plugins/kimchi/ui/pages/help/zh_TW/templates.dita | 112 -
plugins/kimchi/ui/pages/host.html.tmpl | 177 --
plugins/kimchi/ui/pages/i18n.json.tmpl | 187 --
plugins/kimchi/ui/pages/network.html.tmpl | 133 --
plugins/kimchi/ui/pages/report-add.html.tmpl | 56 -
plugins/kimchi/ui/pages/report-rename.html.tmpl | 56 -
plugins/kimchi/ui/pages/repository-add.html.tmpl | 113 -
plugins/kimchi/ui/pages/repository-edit.html.tmpl | 117 -
plugins/kimchi/ui/pages/storage.html.tmpl | 143 --
.../ui/pages/storagepool-add-volume.html.tmpl | 79 -
plugins/kimchi/ui/pages/storagepool-add.html.tmpl | 186 --
plugins/kimchi/ui/pages/template-add.html.tmpl | 233 --
plugins/kimchi/ui/pages/template-edit.html.tmpl | 193 --
plugins/kimchi/ui/pages/templates.html.tmpl | 77 -
plugins/kimchi/ui/robots.txt | 2 -
plugins/kimchi/ui/spice-html5/Makefile.am | 25 -
plugins/kimchi/ui/spice-html5/atKeynames.js | 183 --
plugins/kimchi/ui/spice-html5/bitmap.js | 51 -
plugins/kimchi/ui/spice-html5/css/Makefile.am | 20 -
plugins/kimchi/ui/spice-html5/css/spice.css | 118 -
plugins/kimchi/ui/spice-html5/cursor.js | 110 -
plugins/kimchi/ui/spice-html5/display.js | 823 -------
plugins/kimchi/ui/spice-html5/enums.js | 324 ---
plugins/kimchi/ui/spice-html5/inputs.js | 280 ---
plugins/kimchi/ui/spice-html5/lz.js | 166 --
plugins/kimchi/ui/spice-html5/main.js | 231 --
plugins/kimchi/ui/spice-html5/pages/Makefile.am | 20 -
.../kimchi/ui/spice-html5/pages/spice_auto.html | 200 --
plugins/kimchi/ui/spice-html5/playback.js | 278 ---
plugins/kimchi/ui/spice-html5/png.js | 256 ---
plugins/kimchi/ui/spice-html5/quic.js | 1335 -----------
plugins/kimchi/ui/spice-html5/resize.js | 70 -
plugins/kimchi/ui/spice-html5/simulatecursor.js | 202 --
plugins/kimchi/ui/spice-html5/spicearraybuffer.js | 58 -
plugins/kimchi/ui/spice-html5/spiceconn.js | 460 ----
plugins/kimchi/ui/spice-html5/spicedataview.js | 120 -
plugins/kimchi/ui/spice-html5/spicemsg.js | 1047 ---------
plugins/kimchi/ui/spice-html5/spicetype.js | 473 ----
.../kimchi/ui/spice-html5/thirdparty/Makefile.am | 20 -
plugins/kimchi/ui/spice-html5/thirdparty/jsbn.js | 589 -----
plugins/kimchi/ui/spice-html5/thirdparty/prng4.js | 79 -
plugins/kimchi/ui/spice-html5/thirdparty/rng.js | 102 -
plugins/kimchi/ui/spice-html5/thirdparty/rsa.js | 146 --
plugins/kimchi/ui/spice-html5/thirdparty/sha1.js | 346 ---
plugins/kimchi/ui/spice-html5/ticket.js | 250 ---
plugins/kimchi/ui/spice-html5/utils.js | 265 ---
plugins/kimchi/ui/spice-html5/webm.js | 553 -----
plugins/kimchi/ui/spice-html5/wire.js | 123 -
plugins/kimchi/utils.py | 39 -
plugins/kimchi/vmtemplate.py | 431 ----
plugins/kimchi/vnc.py | 77 -
plugins/kimchi/xmlutils/Makefile.am | 25 -
plugins/kimchi/xmlutils/__init__.py | 18 -
plugins/kimchi/xmlutils/cpu.py | 60 -
plugins/kimchi/xmlutils/disk.py | 164 --
plugins/kimchi/xmlutils/graphics.py | 45 -
plugins/kimchi/xmlutils/interface.py | 61 -
plugins/kimchi/xmlutils/network.py | 122 -
plugins/kimchi/xmlutils/qemucmdline.py | 45 -
plugins/kimchi/yumparser.py | 283 ---
plugins/sample/API.json | 56 -
plugins/sample/Makefile.am | 29 -
plugins/sample/__init__.py | 97 -
plugins/sample/config.status | 1 -
plugins/sample/i18n.py | 40 -
plugins/sample/model.py | 131 --
plugins/sample/po/LINGUAS | 3 -
plugins/sample/po/Makefile.in.in | 400 ----
plugins/sample/po/Makevars | 41 -
plugins/sample/po/POTFILES.in | 2 -
plugins/sample/po/en_US.po | 21 -
plugins/sample/po/gen-pot | 9 -
plugins/sample/po/pt_BR.po | 24 -
plugins/sample/po/sample.pot | 21 -
plugins/sample/po/zh_CN.po | 24 -
plugins/sample/sample.conf.in | 27 -
plugins/sample/ui/Makefile.am | 22 -
plugins/sample/ui/config/Makefile.am | 21 -
plugins/sample/ui/config/tab-ext.xml | 17 -
plugins/sample/ui/css/.gitignore | 0
plugins/sample/ui/images/.gitignore | 0
plugins/sample/ui/js/.gitignore | 0
plugins/sample/ui/js/Makefile.am | 20 -
plugins/sample/ui/js/util.js | 33 -
plugins/sample/ui/libs/.gitignore | 0
plugins/sample/ui/pages/Makefile.am | 20 -
.../sample/ui/pages/help/en_US/sample-tab1.html | 1 -
.../sample/ui/pages/help/en_US/sample-tab2.html | 1 -
plugins/sample/ui/pages/i18n.json.tmpl | 26 -
plugins/sample/ui/pages/sample-tab1.html.tmpl | 30 -
plugins/sample/ui/pages/sample-tab2.html.tmpl | 30 -
src/wok/Makefile.am | 2 +-
src/wok/config.py.in | 7 +-
src/wok/plugins/Makefile.am | 25 +
src/wok/plugins/__init__.py | 18 +
src/wok/plugins/kimchi/.gitignore | 37 +
src/wok/plugins/kimchi/API.json | 836 +++++++
src/wok/plugins/kimchi/CONTRIBUTE.md | 16 +
src/wok/plugins/kimchi/COPYING | 19 +
src/wok/plugins/kimchi/COPYING.ASL2 | 202 ++
src/wok/plugins/kimchi/COPYING.LGPL | 165 ++
src/wok/plugins/kimchi/ChangeLog | 2155 ++++++++++++++++++
src/wok/plugins/kimchi/INSTALL | 369 +++
src/wok/plugins/kimchi/Makefile.am | 165 ++
src/wok/plugins/kimchi/README.md | 1 +
src/wok/plugins/kimchi/VERSION | 1 +
src/wok/plugins/kimchi/__init__.py | 21 +
src/wok/plugins/kimchi/autogen.sh | 21 +
src/wok/plugins/kimchi/build-aux/config.rpath | 672 ++++++
src/wok/plugins/kimchi/build-aux/genChangelog | 25 +
src/wok/plugins/kimchi/build-aux/pkg-version | 59 +
src/wok/plugins/kimchi/config.py.in | 144 ++
src/wok/plugins/kimchi/config.rpath | 672 ++++++
src/wok/plugins/kimchi/configure.ac | 119 +
src/wok/plugins/kimchi/contrib/DEBIAN/Makefile.am | 17 +
src/wok/plugins/kimchi/contrib/DEBIAN/control.in | 30 +
src/wok/plugins/kimchi/contrib/Makefile.am | 34 +
src/wok/plugins/kimchi/contrib/check_i18n.py | 82 +
.../plugins/kimchi/contrib/kimchi.spec.fedora.in | 120 +
src/wok/plugins/kimchi/contrib/kimchi.spec.suse.in | 108 +
src/wok/plugins/kimchi/contrib/make-deb.sh.in | 15 +
src/wok/plugins/kimchi/control/Makefile.am | 27 +
src/wok/plugins/kimchi/control/__init__.py | 26 +
src/wok/plugins/kimchi/control/config.py | 57 +
src/wok/plugins/kimchi/control/cpuinfo.py | 37 +
src/wok/plugins/kimchi/control/debugreports.py | 61 +
src/wok/plugins/kimchi/control/groups.py | 28 +
src/wok/plugins/kimchi/control/host.py | 157 ++
src/wok/plugins/kimchi/control/interfaces.py | 46 +
src/wok/plugins/kimchi/control/networks.py | 54 +
src/wok/plugins/kimchi/control/peers.py | 29 +
src/wok/plugins/kimchi/control/storagepools.py | 116 +
src/wok/plugins/kimchi/control/storageservers.py | 60 +
src/wok/plugins/kimchi/control/storagevolumes.py | 83 +
src/wok/plugins/kimchi/control/templates.py | 58 +
src/wok/plugins/kimchi/control/users.py | 35 +
src/wok/plugins/kimchi/control/vm/Makefile.am | 26 +
src/wok/plugins/kimchi/control/vm/__init__.py | 26 +
src/wok/plugins/kimchi/control/vm/hostdevs.py | 43 +
src/wok/plugins/kimchi/control/vm/ifaces.py | 45 +
src/wok/plugins/kimchi/control/vm/snapshots.py | 58 +
src/wok/plugins/kimchi/control/vm/storages.py | 45 +
src/wok/plugins/kimchi/control/vms.py | 67 +
src/wok/plugins/kimchi/disks.py | 196 ++
src/wok/plugins/kimchi/distroloader.py | 67 +
src/wok/plugins/kimchi/distros.d/Makefile.am | 22 +
src/wok/plugins/kimchi/distros.d/debian.json | 9 +
src/wok/plugins/kimchi/distros.d/fedora.json | 30 +
src/wok/plugins/kimchi/distros.d/gentoo.json | 9 +
src/wok/plugins/kimchi/distros.d/opensuse.json | 23 +
src/wok/plugins/kimchi/distros.d/ubuntu.json | 37 +
src/wok/plugins/kimchi/docs/API.md | 1116 +++++++++
src/wok/plugins/kimchi/docs/Makefile.am | 28 +
src/wok/plugins/kimchi/docs/README-federation.md | 60 +
src/wok/plugins/kimchi/docs/README.md | 247 ++
src/wok/plugins/kimchi/docs/kimchi-guest.png | Bin 0 -> 192281 bytes
src/wok/plugins/kimchi/docs/kimchi-login.png | Bin 0 -> 318041 bytes
src/wok/plugins/kimchi/docs/kimchi-templates.png | Bin 0 -> 329678 bytes
src/wok/plugins/kimchi/i18n.py | 335 +++
src/wok/plugins/kimchi/imageinfo.py | 72 +
src/wok/plugins/kimchi/iscsi.py | 88 +
src/wok/plugins/kimchi/isoinfo.py | 506 +++++
src/wok/plugins/kimchi/kimchi.conf | 37 +
src/wok/plugins/kimchi/kvmusertests.py | 79 +
src/wok/plugins/kimchi/m4/ac_python_module.m4 | 30 +
src/wok/plugins/kimchi/m4/gettext.m4 | 383 ++++
src/wok/plugins/kimchi/m4/iconv.m4 | 214 ++
src/wok/plugins/kimchi/m4/intlmacosx.m4 | 51 +
src/wok/plugins/kimchi/m4/lib-ld.m4 | 110 +
src/wok/plugins/kimchi/m4/lib-link.m4 | 774 +++++++
src/wok/plugins/kimchi/m4/lib-prefix.m4 | 224 ++
src/wok/plugins/kimchi/m4/nls.m4 | 32 +
src/wok/plugins/kimchi/m4/po.m4 | 449 ++++
src/wok/plugins/kimchi/m4/progtest.m4 | 92 +
src/wok/plugins/kimchi/mockmodel.py | 627 ++++++
src/wok/plugins/kimchi/model/Makefile.am | 25 +
src/wok/plugins/kimchi/model/__init__.py | 18 +
src/wok/plugins/kimchi/model/config.py | 176 ++
src/wok/plugins/kimchi/model/cpuinfo.py | 126 ++
src/wok/plugins/kimchi/model/debugreports.py | 213 ++
src/wok/plugins/kimchi/model/diskutils.py | 75 +
src/wok/plugins/kimchi/model/featuretests.py | 259 +++
src/wok/plugins/kimchi/model/groups.py | 67 +
src/wok/plugins/kimchi/model/host.py | 476 ++++
src/wok/plugins/kimchi/model/hostdev.py | 324 +++
src/wok/plugins/kimchi/model/interfaces.py | 44 +
src/wok/plugins/kimchi/model/libvirtconnection.py | 136 ++
src/wok/plugins/kimchi/model/libvirtstoragepool.py | 266 +++
src/wok/plugins/kimchi/model/model.py | 52 +
src/wok/plugins/kimchi/model/networks.py | 382 ++++
src/wok/plugins/kimchi/model/peers.py | 72 +
src/wok/plugins/kimchi/model/storagepools.py | 490 ++++
src/wok/plugins/kimchi/model/storageservers.py | 81 +
src/wok/plugins/kimchi/model/storagetargets.py | 122 +
src/wok/plugins/kimchi/model/storagevolumes.py | 542 +++++
src/wok/plugins/kimchi/model/templates.py | 303 +++
src/wok/plugins/kimchi/model/users.py | 90 +
src/wok/plugins/kimchi/model/utils.py | 161 ++
src/wok/plugins/kimchi/model/vmhostdevs.py | 336 +++
src/wok/plugins/kimchi/model/vmifaces.py | 186 ++
src/wok/plugins/kimchi/model/vms.py | 1307 +++++++++++
src/wok/plugins/kimchi/model/vmsnapshots.py | 204 ++
src/wok/plugins/kimchi/model/vmstorages.py | 252 +++
src/wok/plugins/kimchi/netinfo.py | 216 ++
src/wok/plugins/kimchi/network.py | 62 +
src/wok/plugins/kimchi/osinfo.py | 213 ++
src/wok/plugins/kimchi/po/LINGUAS | 11 +
src/wok/plugins/kimchi/po/Makefile.in.in | 398 ++++
src/wok/plugins/kimchi/po/Makevars | 41 +
src/wok/plugins/kimchi/po/POTFILES.in | 3 +
src/wok/plugins/kimchi/po/de_DE.po | 2288 +++++++++++++++++++
src/wok/plugins/kimchi/po/en_US.po | 2075 +++++++++++++++++
src/wok/plugins/kimchi/po/es_ES.po | 2305 +++++++++++++++++++
src/wok/plugins/kimchi/po/fr_FR.po | 2338 +++++++++++++++++++
src/wok/plugins/kimchi/po/gen-pot.in | 9 +
src/wok/plugins/kimchi/po/it_IT.po | 2274 +++++++++++++++++++
src/wok/plugins/kimchi/po/ja_JP.po | 2269 +++++++++++++++++++
src/wok/plugins/kimchi/po/kimchi.pot | 2074 +++++++++++++++++
src/wok/plugins/kimchi/po/ko_KR.po | 2197 ++++++++++++++++++
src/wok/plugins/kimchi/po/pt_BR.po | 2369 ++++++++++++++++++++
src/wok/plugins/kimchi/po/ru_RU.po | 2198 ++++++++++++++++++
src/wok/plugins/kimchi/po/zh_CN.po | 2186 ++++++++++++++++++
src/wok/plugins/kimchi/po/zh_TW.po | 2138 ++++++++++++++++++
src/wok/plugins/kimchi/repositories.py | 529 +++++
src/wok/plugins/kimchi/root.py | 69 +
src/wok/plugins/kimchi/scan.py | 89 +
src/wok/plugins/kimchi/screenshot.py | 184 ++
src/wok/plugins/kimchi/swupdate.py | 263 +++
src/wok/plugins/kimchi/template.conf | 47 +
src/wok/plugins/kimchi/tests/Makefile.am | 50 +
src/wok/plugins/kimchi/tests/iso_gen.py | 212 ++
src/wok/plugins/kimchi/tests/run_tests.sh.in | 55 +
src/wok/plugins/kimchi/tests/test_authorization.py | 178 ++
src/wok/plugins/kimchi/tests/test_config.py.in | 267 +++
src/wok/plugins/kimchi/tests/test_host.py | 206 ++
src/wok/plugins/kimchi/tests/test_mock_network.py | 73 +
.../plugins/kimchi/tests/test_mock_storagepool.py | 147 ++
.../kimchi/tests/test_mock_storagevolume.py | 98 +
src/wok/plugins/kimchi/tests/test_mockmodel.py | 141 ++
src/wok/plugins/kimchi/tests/test_model.py | 1248 +++++++++++
src/wok/plugins/kimchi/tests/test_model_network.py | 149 ++
.../plugins/kimchi/tests/test_model_storagepool.py | 123 +
.../kimchi/tests/test_model_storagevolume.py | 280 +++
src/wok/plugins/kimchi/tests/test_networkxml.py | 172 ++
src/wok/plugins/kimchi/tests/test_osinfo.py | 69 +
src/wok/plugins/kimchi/tests/test_rest.py | 1327 +++++++++++
.../plugins/kimchi/tests/test_storagepoolxml.py | 171 ++
src/wok/plugins/kimchi/tests/test_template.py | 387 ++++
src/wok/plugins/kimchi/tests/test_vmtemplate.py | 116 +
src/wok/plugins/kimchi/tests/test_yumparser.py | 162 ++
src/wok/plugins/kimchi/tests/utils.py | 260 +++
src/wok/plugins/kimchi/ui/Makefile.am | 20 +
src/wok/plugins/kimchi/ui/config/Makefile.am | 22 +
src/wok/plugins/kimchi/ui/config/tab-ext.xml | 38 +
src/wok/plugins/kimchi/ui/css/Makefile.am | 26 +
.../kimchi/ui/css/theme-default/guest-edit.css | 424 ++++
.../ui/css/theme-default/guest-storage-add.css | 81 +
.../plugins/kimchi/ui/css/theme-default/host.css | 287 +++
.../plugins/kimchi/ui/css/theme-default/icon.css | 106 +
.../plugins/kimchi/ui/css/theme-default/list.css | 326 +++
.../kimchi/ui/css/theme-default/network.css | 267 +++
.../kimchi/ui/css/theme-default/report-add.css | 37 +
.../kimchi/ui/css/theme-default/report-rename.css | 39 +
.../kimchi/ui/css/theme-default/repository-add.css | 42 +
.../ui/css/theme-default/repository-edit.css | 88 +
.../kimchi/ui/css/theme-default/storage.css | 550 +++++
.../css/theme-default/storagepool-add-volume.css | 36 +
.../kimchi/ui/css/theme-default/template-edit.css | 175 ++
.../kimchi/ui/css/theme-default/template.css | 85 +
.../kimchi/ui/css/theme-default/template_add.css | 317 +++
.../kimchi/ui/css/theme-default/template_list.css | 267 +++
src/wok/plugins/kimchi/ui/images/Makefile.am | 22 +
src/wok/plugins/kimchi/ui/images/icon-centos.png | Bin 0 -> 4734 bytes
src/wok/plugins/kimchi/ui/images/icon-debian.png | Bin 0 -> 4239 bytes
src/wok/plugins/kimchi/ui/images/icon-fedora.png | Bin 0 -> 4449 bytes
src/wok/plugins/kimchi/ui/images/icon-gentoo.png | Bin 0 -> 15307 bytes
src/wok/plugins/kimchi/ui/images/icon-opensuse.png | Bin 0 -> 3046 bytes
src/wok/plugins/kimchi/ui/images/icon-ubuntu.png | Bin 0 -> 4818 bytes
src/wok/plugins/kimchi/ui/images/icon-vm.png | Bin 0 -> 2976 bytes
.../kimchi/ui/images/theme-default/Makefile.am | 20 +
.../kimchi/ui/images/theme-default/ac22_pause.png | Bin 0 -> 1219 bytes
.../ui/images/theme-default/ac22_pause_grey.png | Bin 0 -> 1175 bytes
.../kimchi/ui/images/theme-default/ac24_resume.png | Bin 0 -> 1341 bytes
.../ui/images/theme-default/ac24_resume_grey.png | Bin 0 -> 1282 bytes
.../ui/images/theme-default/arrow-down-black.png | Bin 0 -> 2942 bytes
.../ui/images/theme-default/arrow-down-disable.png | Bin 0 -> 472 bytes
.../kimchi/ui/images/theme-default/arrow-down.png | Bin 0 -> 537 bytes
.../kimchi/ui/images/theme-default/arrow-up.png | Bin 0 -> 510 bytes
.../kimchi/ui/images/theme-default/arrow_out.png | Bin 0 -> 3048 bytes
.../kimchi/ui/images/theme-default/group.png | Bin 0 -> 1703 bytes
.../ui/images/theme-default/host-icon-sprite.png | Bin 0 -> 1034 bytes
.../kimchi/ui/images/theme-default/icon-back.png | Bin 0 -> 244 bytes
.../kimchi/ui/images/theme-default/icon-camera.png | Bin 0 -> 4860 bytes
.../kimchi/ui/images/theme-default/icon-design.png | Bin 0 -> 4562 bytes
.../kimchi/ui/images/theme-default/icon-detail.png | Bin 0 -> 3079 bytes
.../kimchi/ui/images/theme-default/icon-iso.png | Bin 0 -> 4188 bytes
.../kimchi/ui/images/theme-default/icon-list.png | Bin 0 -> 2983 bytes
.../kimchi/ui/images/theme-default/icon-load.png | Bin 0 -> 3678 bytes
.../kimchi/ui/images/theme-default/icon-local.png | Bin 0 -> 425 bytes
.../ui/images/theme-default/icon-power-down.png | Bin 0 -> 4372 bytes
.../ui/images/theme-default/icon-power-up.png | Bin 0 -> 4367 bytes
.../kimchi/ui/images/theme-default/icon-qcow2.png | Bin 0 -> 4684 bytes
.../kimchi/ui/images/theme-default/icon-raw.png | Bin 0 -> 4679 bytes
.../kimchi/ui/images/theme-default/icon-remote.png | Bin 0 -> 1005 bytes
.../kimchi/ui/images/theme-default/icon-reset.png | Bin 0 -> 4576 bytes
.../kimchi/ui/images/theme-default/icon-search.png | Bin 0 -> 4197 bytes
.../kimchi/ui/images/theme-default/icon-sort.png | Bin 0 -> 3421 bytes
.../kimchi/ui/images/theme-default/icon-tree.png | Bin 0 -> 3526 bytes
.../kimchi/ui/images/theme-default/icon-user.png | Bin 0 -> 5366 bytes
.../images/theme-default/icon-volume-default.png | Bin 0 -> 4265 bytes
.../images/theme-default/kimchi-loading15x15.gif | Bin 0 -> 1653 bytes
.../kimchi/ui/images/theme-default/loading.gif | Bin 0 -> 2190 bytes
.../kimchi/ui/images/theme-default/user.png | Bin 0 -> 1322 bytes
src/wok/plugins/kimchi/ui/js/Makefile.am | 27 +
src/wok/plugins/kimchi/ui/js/src/kimchi.api.js | 1355 +++++++++++
.../kimchi/ui/js/src/kimchi.guest_add_main.js | 86 +
.../kimchi/ui/js/src/kimchi.guest_edit_main.js | 759 +++++++
.../plugins/kimchi/ui/js/src/kimchi.guest_main.js | 511 +++++
.../kimchi/ui/js/src/kimchi.guest_media_main.js | 56 +
.../ui/js/src/kimchi.guest_storage_add.main.js | 199 ++
src/wok/plugins/kimchi/ui/js/src/kimchi.host.js | 858 +++++++
src/wok/plugins/kimchi/ui/js/src/kimchi.main.js | 26 +
src/wok/plugins/kimchi/ui/js/src/kimchi.network.js | 442 ++++
.../kimchi/ui/js/src/kimchi.report_add_main.js | 72 +
.../kimchi/ui/js/src/kimchi.report_rename_main.js | 66 +
.../kimchi/ui/js/src/kimchi.repository_add_main.js | 96 +
.../ui/js/src/kimchi.repository_edit_main.js | 74 +
.../kimchi/ui/js/src/kimchi.storage_main.js | 428 ++++
.../ui/js/src/kimchi.storagepool_add_main.js | 414 ++++
.../js/src/kimchi.storagepool_add_volume_main.js | 179 ++
.../kimchi/ui/js/src/kimchi.template_add_main.js | 441 ++++
.../kimchi/ui/js/src/kimchi.template_edit_main.js | 343 +++
.../kimchi/ui/js/src/kimchi.template_main.js | 111 +
src/wok/plugins/kimchi/ui/pages/Makefile.am | 22 +
.../plugins/kimchi/ui/pages/guest-add.html.tmpl | 98 +
.../plugins/kimchi/ui/pages/guest-edit.html.tmpl | 311 +++
.../kimchi/ui/pages/guest-storage-add.html.tmpl | 103 +
src/wok/plugins/kimchi/ui/pages/guest.html.tmpl | 77 +
src/wok/plugins/kimchi/ui/pages/guests.html.tmpl | 65 +
src/wok/plugins/kimchi/ui/pages/help/Makefile.am | 34 +
.../plugins/kimchi/ui/pages/help/de_DE/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/de_DE/guests.dita | 127 ++
.../plugins/kimchi/ui/pages/help/de_DE/host.dita | 49 +
.../kimchi/ui/pages/help/de_DE/network.dita | 62 +
.../kimchi/ui/pages/help/de_DE/storage.dita | 86 +
.../kimchi/ui/pages/help/de_DE/templates.dita | 112 +
src/wok/plugins/kimchi/ui/pages/help/dita-help.xsl | 26 +
.../plugins/kimchi/ui/pages/help/en_US/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/en_US/guests.dita | 136 ++
.../plugins/kimchi/ui/pages/help/en_US/host.dita | 70 +
.../kimchi/ui/pages/help/en_US/network.dita | 68 +
.../kimchi/ui/pages/help/en_US/storage.dita | 99 +
.../kimchi/ui/pages/help/en_US/templates.dita | 123 +
.../plugins/kimchi/ui/pages/help/es_ES/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/es_ES/guests.dita | 120 +
.../plugins/kimchi/ui/pages/help/es_ES/host.dita | 49 +
.../kimchi/ui/pages/help/es_ES/network.dita | 61 +
.../kimchi/ui/pages/help/es_ES/storage.dita | 86 +
.../kimchi/ui/pages/help/es_ES/templates.dita | 111 +
.../plugins/kimchi/ui/pages/help/fr_FR/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/fr_FR/guests.dita | 130 ++
.../plugins/kimchi/ui/pages/help/fr_FR/host.dita | 68 +
.../kimchi/ui/pages/help/fr_FR/network.dita | 67 +
.../kimchi/ui/pages/help/fr_FR/storage.dita | 93 +
.../kimchi/ui/pages/help/fr_FR/templates.dita | 120 +
.../plugins/kimchi/ui/pages/help/it_IT/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/it_IT/guests.dita | 123 +
.../plugins/kimchi/ui/pages/help/it_IT/host.dita | 51 +
.../kimchi/ui/pages/help/it_IT/network.dita | 63 +
.../kimchi/ui/pages/help/it_IT/storage.dita | 91 +
.../kimchi/ui/pages/help/it_IT/templates.dita | 115 +
.../plugins/kimchi/ui/pages/help/ja_JP/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/ja_JP/guests.dita | 172 ++
.../plugins/kimchi/ui/pages/help/ja_JP/host.dita | 70 +
.../kimchi/ui/pages/help/ja_JP/network.dita | 83 +
.../kimchi/ui/pages/help/ja_JP/storage.dita | 120 +
.../kimchi/ui/pages/help/ja_JP/templates.dita | 150 ++
src/wok/plugins/kimchi/ui/pages/help/kimchi.css | 208 ++
.../plugins/kimchi/ui/pages/help/ko_KR/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/ko_KR/guests.dita | 119 +
.../plugins/kimchi/ui/pages/help/ko_KR/host.dita | 47 +
.../kimchi/ui/pages/help/ko_KR/network.dita | 61 +
.../kimchi/ui/pages/help/ko_KR/storage.dita | 86 +
.../kimchi/ui/pages/help/ko_KR/templates.dita | 111 +
.../plugins/kimchi/ui/pages/help/pt_BR/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/pt_BR/guests.dita | 137 ++
.../plugins/kimchi/ui/pages/help/pt_BR/host.dita | 74 +
.../kimchi/ui/pages/help/pt_BR/network.dita | 72 +
.../kimchi/ui/pages/help/pt_BR/storage.dita | 102 +
.../kimchi/ui/pages/help/pt_BR/templates.dita | 127 ++
.../plugins/kimchi/ui/pages/help/ru_RU/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/ru_RU/guests.dita | 122 +
.../plugins/kimchi/ui/pages/help/ru_RU/host.dita | 48 +
.../kimchi/ui/pages/help/ru_RU/network.dita | 61 +
.../kimchi/ui/pages/help/ru_RU/storage.dita | 88 +
.../kimchi/ui/pages/help/ru_RU/templates.dita | 111 +
.../plugins/kimchi/ui/pages/help/zh_CN/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/zh_CN/guests.dita | 118 +
.../plugins/kimchi/ui/pages/help/zh_CN/host.dita | 45 +
.../kimchi/ui/pages/help/zh_CN/network.dita | 61 +
.../kimchi/ui/pages/help/zh_CN/storage.dita | 84 +
.../kimchi/ui/pages/help/zh_CN/templates.dita | 111 +
.../plugins/kimchi/ui/pages/help/zh_TW/Makefile.am | 23 +
.../plugins/kimchi/ui/pages/help/zh_TW/guests.dita | 120 +
.../plugins/kimchi/ui/pages/help/zh_TW/host.dita | 50 +
.../kimchi/ui/pages/help/zh_TW/network.dita | 61 +
.../kimchi/ui/pages/help/zh_TW/storage.dita | 88 +
.../kimchi/ui/pages/help/zh_TW/templates.dita | 112 +
src/wok/plugins/kimchi/ui/pages/host.html.tmpl | 177 ++
src/wok/plugins/kimchi/ui/pages/i18n.json.tmpl | 187 ++
src/wok/plugins/kimchi/ui/pages/network.html.tmpl | 133 ++
.../plugins/kimchi/ui/pages/report-add.html.tmpl | 56 +
.../kimchi/ui/pages/report-rename.html.tmpl | 56 +
.../kimchi/ui/pages/repository-add.html.tmpl | 113 +
.../kimchi/ui/pages/repository-edit.html.tmpl | 117 +
src/wok/plugins/kimchi/ui/pages/storage.html.tmpl | 143 ++
.../ui/pages/storagepool-add-volume.html.tmpl | 79 +
.../kimchi/ui/pages/storagepool-add.html.tmpl | 186 ++
.../plugins/kimchi/ui/pages/template-add.html.tmpl | 233 ++
.../kimchi/ui/pages/template-edit.html.tmpl | 193 ++
.../plugins/kimchi/ui/pages/templates.html.tmpl | 77 +
src/wok/plugins/kimchi/ui/robots.txt | 2 +
src/wok/plugins/kimchi/ui/spice-html5/Makefile.am | 25 +
.../plugins/kimchi/ui/spice-html5/atKeynames.js | 183 ++
src/wok/plugins/kimchi/ui/spice-html5/bitmap.js | 51 +
.../plugins/kimchi/ui/spice-html5/css/Makefile.am | 20 +
.../plugins/kimchi/ui/spice-html5/css/spice.css | 118 +
src/wok/plugins/kimchi/ui/spice-html5/cursor.js | 110 +
src/wok/plugins/kimchi/ui/spice-html5/display.js | 823 +++++++
src/wok/plugins/kimchi/ui/spice-html5/enums.js | 324 +++
src/wok/plugins/kimchi/ui/spice-html5/inputs.js | 280 +++
src/wok/plugins/kimchi/ui/spice-html5/lz.js | 166 ++
src/wok/plugins/kimchi/ui/spice-html5/main.js | 231 ++
.../kimchi/ui/spice-html5/pages/Makefile.am | 20 +
.../kimchi/ui/spice-html5/pages/spice_auto.html | 200 ++
src/wok/plugins/kimchi/ui/spice-html5/playback.js | 278 +++
src/wok/plugins/kimchi/ui/spice-html5/png.js | 256 +++
src/wok/plugins/kimchi/ui/spice-html5/quic.js | 1335 +++++++++++
src/wok/plugins/kimchi/ui/spice-html5/resize.js | 70 +
.../kimchi/ui/spice-html5/simulatecursor.js | 202 ++
.../kimchi/ui/spice-html5/spicearraybuffer.js | 58 +
src/wok/plugins/kimchi/ui/spice-html5/spiceconn.js | 460 ++++
.../plugins/kimchi/ui/spice-html5/spicedataview.js | 120 +
src/wok/plugins/kimchi/ui/spice-html5/spicemsg.js | 1047 +++++++++
src/wok/plugins/kimchi/ui/spice-html5/spicetype.js | 473 ++++
.../kimchi/ui/spice-html5/thirdparty/Makefile.am | 20 +
.../kimchi/ui/spice-html5/thirdparty/jsbn.js | 589 +++++
.../kimchi/ui/spice-html5/thirdparty/prng4.js | 79 +
.../kimchi/ui/spice-html5/thirdparty/rng.js | 102 +
.../kimchi/ui/spice-html5/thirdparty/rsa.js | 146 ++
.../kimchi/ui/spice-html5/thirdparty/sha1.js | 346 +++
src/wok/plugins/kimchi/ui/spice-html5/ticket.js | 250 +++
src/wok/plugins/kimchi/ui/spice-html5/utils.js | 265 +++
src/wok/plugins/kimchi/ui/spice-html5/webm.js | 553 +++++
src/wok/plugins/kimchi/ui/spice-html5/wire.js | 123 +
src/wok/plugins/kimchi/utils.py | 39 +
src/wok/plugins/kimchi/vmtemplate.py | 431 ++++
src/wok/plugins/kimchi/vnc.py | 77 +
src/wok/plugins/kimchi/xmlutils/Makefile.am | 25 +
src/wok/plugins/kimchi/xmlutils/__init__.py | 18 +
src/wok/plugins/kimchi/xmlutils/cpu.py | 60 +
src/wok/plugins/kimchi/xmlutils/disk.py | 164 ++
src/wok/plugins/kimchi/xmlutils/graphics.py | 45 +
src/wok/plugins/kimchi/xmlutils/interface.py | 61 +
src/wok/plugins/kimchi/xmlutils/network.py | 122 +
src/wok/plugins/kimchi/xmlutils/qemucmdline.py | 45 +
src/wok/plugins/kimchi/yumparser.py | 283 +++
src/wok/plugins/sample/API.json | 56 +
src/wok/plugins/sample/Makefile.am | 29 +
src/wok/plugins/sample/__init__.py | 97 +
src/wok/plugins/sample/config.status | 1 +
src/wok/plugins/sample/i18n.py | 40 +
src/wok/plugins/sample/model.py | 131 ++
src/wok/plugins/sample/po/LINGUAS | 3 +
src/wok/plugins/sample/po/Makefile.in.in | 400 ++++
src/wok/plugins/sample/po/Makevars | 41 +
src/wok/plugins/sample/po/POTFILES.in | 2 +
src/wok/plugins/sample/po/en_US.po | 21 +
src/wok/plugins/sample/po/gen-pot | 9 +
src/wok/plugins/sample/po/pt_BR.po | 24 +
src/wok/plugins/sample/po/sample.pot | 21 +
src/wok/plugins/sample/po/zh_CN.po | 24 +
src/wok/plugins/sample/sample.conf.in | 27 +
src/wok/plugins/sample/ui/Makefile.am | 22 +
src/wok/plugins/sample/ui/config/Makefile.am | 21 +
src/wok/plugins/sample/ui/config/tab-ext.xml | 17 +
src/wok/plugins/sample/ui/css/.gitignore | 0
src/wok/plugins/sample/ui/images/.gitignore | 0
src/wok/plugins/sample/ui/js/.gitignore | 0
src/wok/plugins/sample/ui/js/Makefile.am | 20 +
src/wok/plugins/sample/ui/js/util.js | 33 +
src/wok/plugins/sample/ui/libs/.gitignore | 0
src/wok/plugins/sample/ui/pages/Makefile.am | 20 +
.../sample/ui/pages/help/en_US/sample-tab1.html | 1 +
.../sample/ui/pages/help/en_US/sample-tab2.html | 1 +
src/wok/plugins/sample/ui/pages/i18n.json.tmpl | 26 +
.../plugins/sample/ui/pages/sample-tab1.html.tmpl | 30 +
.../plugins/sample/ui/pages/sample-tab2.html.tmpl | 30 +
809 files changed, 84521 insertions(+), 81955 deletions(-)
delete mode 100644 plugins/Makefile.am
delete mode 100644 plugins/__init__.py
delete mode 100644 plugins/kimchi/.gitignore
delete mode 100644 plugins/kimchi/API.json
delete mode 100644 plugins/kimchi/INSTALL
delete mode 100644 plugins/kimchi/Makefile.am
delete mode 120000 plugins/kimchi/README.md
delete mode 100644 plugins/kimchi/VERSION
delete mode 100644 plugins/kimchi/__init__.py
delete mode 100755 plugins/kimchi/autogen.sh
delete mode 100644 plugins/kimchi/build-aux/config.rpath
delete mode 100755 plugins/kimchi/build-aux/genChangelog
delete mode 100755 plugins/kimchi/build-aux/pkg-version
delete mode 100644 plugins/kimchi/config.py.in
delete mode 100644 plugins/kimchi/config.rpath
delete mode 100644 plugins/kimchi/configure.ac
delete mode 100644 plugins/kimchi/contrib/DEBIAN/Makefile.am
delete mode 100644 plugins/kimchi/contrib/DEBIAN/control.in
delete mode 100644 plugins/kimchi/contrib/Makefile.am
delete mode 100755 plugins/kimchi/contrib/check_i18n.py
delete mode 100644 plugins/kimchi/contrib/kimchi.spec.fedora.in
delete mode 100644 plugins/kimchi/contrib/kimchi.spec.suse.in
delete mode 100644 plugins/kimchi/contrib/make-deb.sh.in
delete mode 100644 plugins/kimchi/control/Makefile.am
delete mode 100644 plugins/kimchi/control/__init__.py
delete mode 100644 plugins/kimchi/control/config.py
delete mode 100644 plugins/kimchi/control/cpuinfo.py
delete mode 100644 plugins/kimchi/control/debugreports.py
delete mode 100644 plugins/kimchi/control/groups.py
delete mode 100644 plugins/kimchi/control/host.py
delete mode 100644 plugins/kimchi/control/interfaces.py
delete mode 100644 plugins/kimchi/control/networks.py
delete mode 100644 plugins/kimchi/control/peers.py
delete mode 100644 plugins/kimchi/control/storagepools.py
delete mode 100644 plugins/kimchi/control/storageservers.py
delete mode 100644 plugins/kimchi/control/storagevolumes.py
delete mode 100644 plugins/kimchi/control/templates.py
delete mode 100644 plugins/kimchi/control/users.py
delete mode 100644 plugins/kimchi/control/vm/Makefile.am
delete mode 100644 plugins/kimchi/control/vm/__init__.py
delete mode 100644 plugins/kimchi/control/vm/hostdevs.py
delete mode 100644 plugins/kimchi/control/vm/ifaces.py
delete mode 100644 plugins/kimchi/control/vm/snapshots.py
delete mode 100644 plugins/kimchi/control/vm/storages.py
delete mode 100644 plugins/kimchi/control/vms.py
delete mode 100644 plugins/kimchi/disks.py
delete mode 100644 plugins/kimchi/distroloader.py
delete mode 100644 plugins/kimchi/distros.d/Makefile.am
delete mode 100644 plugins/kimchi/distros.d/debian.json
delete mode 100644 plugins/kimchi/distros.d/fedora.json
delete mode 100644 plugins/kimchi/distros.d/gentoo.json
delete mode 100644 plugins/kimchi/distros.d/opensuse.json
delete mode 100644 plugins/kimchi/distros.d/ubuntu.json
delete mode 100644 plugins/kimchi/docs/API.md
delete mode 100644 plugins/kimchi/docs/Makefile.am
delete mode 100644 plugins/kimchi/docs/README-federation.md
delete mode 100644 plugins/kimchi/docs/README.md
delete mode 100644 plugins/kimchi/docs/kimchi-guest.png
delete mode 100644 plugins/kimchi/docs/kimchi-login.png
delete mode 100644 plugins/kimchi/docs/kimchi-templates.png
delete mode 100644 plugins/kimchi/i18n.py
delete mode 100644 plugins/kimchi/imageinfo.py
delete mode 100644 plugins/kimchi/iscsi.py
delete mode 100644 plugins/kimchi/isoinfo.py
delete mode 100644 plugins/kimchi/kimchi.conf
delete mode 100644 plugins/kimchi/kvmusertests.py
delete mode 100644 plugins/kimchi/m4/ac_python_module.m4
delete mode 100644 plugins/kimchi/m4/gettext.m4
delete mode 100644 plugins/kimchi/m4/iconv.m4
delete mode 100644 plugins/kimchi/m4/intlmacosx.m4
delete mode 100644 plugins/kimchi/m4/lib-ld.m4
delete mode 100644 plugins/kimchi/m4/lib-link.m4
delete mode 100644 plugins/kimchi/m4/lib-prefix.m4
delete mode 100644 plugins/kimchi/m4/nls.m4
delete mode 100644 plugins/kimchi/m4/po.m4
delete mode 100644 plugins/kimchi/m4/progtest.m4
delete mode 100644 plugins/kimchi/mockmodel.py
delete mode 100644 plugins/kimchi/model/Makefile.am
delete mode 100644 plugins/kimchi/model/__init__.py
delete mode 100644 plugins/kimchi/model/config.py
delete mode 100644 plugins/kimchi/model/cpuinfo.py
delete mode 100644 plugins/kimchi/model/debugreports.py
delete mode 100644 plugins/kimchi/model/diskutils.py
delete mode 100644 plugins/kimchi/model/featuretests.py
delete mode 100644 plugins/kimchi/model/groups.py
delete mode 100644 plugins/kimchi/model/host.py
delete mode 100644 plugins/kimchi/model/hostdev.py
delete mode 100644 plugins/kimchi/model/interfaces.py
delete mode 100644 plugins/kimchi/model/libvirtconnection.py
delete mode 100644 plugins/kimchi/model/libvirtstoragepool.py
delete mode 100644 plugins/kimchi/model/model.py
delete mode 100644 plugins/kimchi/model/networks.py
delete mode 100644 plugins/kimchi/model/peers.py
delete mode 100644 plugins/kimchi/model/storagepools.py
delete mode 100644 plugins/kimchi/model/storageservers.py
delete mode 100644 plugins/kimchi/model/storagetargets.py
delete mode 100644 plugins/kimchi/model/storagevolumes.py
delete mode 100644 plugins/kimchi/model/templates.py
delete mode 100644 plugins/kimchi/model/users.py
delete mode 100644 plugins/kimchi/model/utils.py
delete mode 100644 plugins/kimchi/model/vmhostdevs.py
delete mode 100644 plugins/kimchi/model/vmifaces.py
delete mode 100644 plugins/kimchi/model/vms.py
delete mode 100644 plugins/kimchi/model/vmsnapshots.py
delete mode 100644 plugins/kimchi/model/vmstorages.py
delete mode 100644 plugins/kimchi/netinfo.py
delete mode 100644 plugins/kimchi/network.py
delete mode 100644 plugins/kimchi/osinfo.py
delete mode 100644 plugins/kimchi/po/LINGUAS
delete mode 100644 plugins/kimchi/po/Makefile.in.in
delete mode 100644 plugins/kimchi/po/Makevars
delete mode 100644 plugins/kimchi/po/POTFILES.in
delete mode 100644 plugins/kimchi/po/de_DE.po
delete mode 100644 plugins/kimchi/po/en_US.po
delete mode 100644 plugins/kimchi/po/es_ES.po
delete mode 100644 plugins/kimchi/po/fr_FR.po
delete mode 100644 plugins/kimchi/po/gen-pot.in
delete mode 100644 plugins/kimchi/po/it_IT.po
delete mode 100644 plugins/kimchi/po/ja_JP.po
delete mode 100755 plugins/kimchi/po/kimchi.pot
delete mode 100644 plugins/kimchi/po/ko_KR.po
delete mode 100644 plugins/kimchi/po/pt_BR.po
delete mode 100644 plugins/kimchi/po/ru_RU.po
delete mode 100644 plugins/kimchi/po/zh_CN.po
delete mode 100644 plugins/kimchi/po/zh_TW.po
delete mode 100644 plugins/kimchi/repositories.py
delete mode 100644 plugins/kimchi/root.py
delete mode 100644 plugins/kimchi/scan.py
delete mode 100644 plugins/kimchi/screenshot.py
delete mode 100644 plugins/kimchi/swupdate.py
delete mode 100644 plugins/kimchi/template.conf
delete mode 100644 plugins/kimchi/tests/Makefile.am
delete mode 100644 plugins/kimchi/tests/iso_gen.py
delete mode 100644 plugins/kimchi/tests/run_tests.sh.in
delete mode 100644 plugins/kimchi/tests/test_authorization.py
delete mode 100644 plugins/kimchi/tests/test_config.py.in
delete mode 100644 plugins/kimchi/tests/test_host.py
delete mode 100644 plugins/kimchi/tests/test_mock_network.py
delete mode 100644 plugins/kimchi/tests/test_mock_storagepool.py
delete mode 100644 plugins/kimchi/tests/test_mock_storagevolume.py
delete mode 100644 plugins/kimchi/tests/test_mockmodel.py
delete mode 100644 plugins/kimchi/tests/test_model.py
delete mode 100644 plugins/kimchi/tests/test_model_network.py
delete mode 100644 plugins/kimchi/tests/test_model_storagepool.py
delete mode 100644 plugins/kimchi/tests/test_model_storagevolume.py
delete mode 100644 plugins/kimchi/tests/test_networkxml.py
delete mode 100644 plugins/kimchi/tests/test_osinfo.py
delete mode 100644 plugins/kimchi/tests/test_rest.py
delete mode 100644 plugins/kimchi/tests/test_storagepoolxml.py
delete mode 100644 plugins/kimchi/tests/test_template.py
delete mode 100644 plugins/kimchi/tests/test_vmtemplate.py
delete mode 100644 plugins/kimchi/tests/test_yumparser.py
delete mode 100644 plugins/kimchi/tests/utils.py
delete mode 100644 plugins/kimchi/ui/Makefile.am
delete mode 100644 plugins/kimchi/ui/config/Makefile.am
delete mode 100644 plugins/kimchi/ui/config/tab-ext.xml
delete mode 100644 plugins/kimchi/ui/css/Makefile.am
delete mode 100644 plugins/kimchi/ui/css/theme-default/guest-edit.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/guest-storage-add.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/host.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/icon.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/list.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/network.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/report-add.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/report-rename.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/repository-add.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/repository-edit.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/storage.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/storagepool-add-volume.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/template-edit.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/template.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/template_add.css
delete mode 100644 plugins/kimchi/ui/css/theme-default/template_list.css
delete mode 100644 plugins/kimchi/ui/images/Makefile.am
delete mode 100644 plugins/kimchi/ui/images/icon-centos.png
delete mode 100644 plugins/kimchi/ui/images/icon-debian.png
delete mode 100644 plugins/kimchi/ui/images/icon-fedora.png
delete mode 100644 plugins/kimchi/ui/images/icon-gentoo.png
delete mode 100644 plugins/kimchi/ui/images/icon-opensuse.png
delete mode 100644 plugins/kimchi/ui/images/icon-ubuntu.png
delete mode 100644 plugins/kimchi/ui/images/icon-vm.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/Makefile.am
delete mode 100644 plugins/kimchi/ui/images/theme-default/ac22_pause.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/ac22_pause_grey.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/ac24_resume.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/ac24_resume_grey.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/arrow-down-black.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/arrow-down-disable.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/arrow-down.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/arrow-up.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/arrow_out.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/group.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/host-icon-sprite.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-back.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-camera.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-design.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-detail.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-iso.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-list.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-load.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-local.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-power-down.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-power-up.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-qcow2.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-raw.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-remote.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-reset.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-search.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-sort.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-tree.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-user.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/icon-volume-default.png
delete mode 100644 plugins/kimchi/ui/images/theme-default/kimchi-loading15x15.gif
delete mode 100644 plugins/kimchi/ui/images/theme-default/loading.gif
delete mode 100644 plugins/kimchi/ui/images/theme-default/user.png
delete mode 100644 plugins/kimchi/ui/js/Makefile.am
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.api.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.guest_add_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.guest_edit_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.guest_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.guest_media_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.guest_storage_add.main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.host.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.network.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.report_add_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.report_rename_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.repository_add_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.repository_edit_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.storage_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.storagepool_add_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.storagepool_add_volume_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.template_add_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.template_edit_main.js
delete mode 100644 plugins/kimchi/ui/js/src/kimchi.template_main.js
delete mode 100644 plugins/kimchi/ui/pages/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/guest-add.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/guest-edit.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/guest-storage-add.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/guest.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/guests.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/help/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/de_DE/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/de_DE/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/de_DE/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/de_DE/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/de_DE/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/de_DE/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/dita-help.xsl
delete mode 100644 plugins/kimchi/ui/pages/help/en_US/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/en_US/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/en_US/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/en_US/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/en_US/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/en_US/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/es_ES/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/es_ES/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/es_ES/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/es_ES/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/es_ES/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/es_ES/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/fr_FR/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/fr_FR/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/fr_FR/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/fr_FR/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/fr_FR/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/fr_FR/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/it_IT/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/it_IT/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/it_IT/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/it_IT/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/it_IT/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/it_IT/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ja_JP/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/ja_JP/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ja_JP/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ja_JP/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ja_JP/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ja_JP/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/kimchi.css
delete mode 100644 plugins/kimchi/ui/pages/help/ko_KR/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/ko_KR/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ko_KR/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ko_KR/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ko_KR/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ko_KR/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/pt_BR/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/pt_BR/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/pt_BR/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/pt_BR/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/pt_BR/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/pt_BR/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ru_RU/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/ru_RU/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ru_RU/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ru_RU/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ru_RU/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/ru_RU/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_CN/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/zh_CN/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_CN/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_CN/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_CN/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_CN/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_TW/Makefile.am
delete mode 100644 plugins/kimchi/ui/pages/help/zh_TW/guests.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_TW/host.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_TW/network.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_TW/storage.dita
delete mode 100644 plugins/kimchi/ui/pages/help/zh_TW/templates.dita
delete mode 100644 plugins/kimchi/ui/pages/host.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/i18n.json.tmpl
delete mode 100644 plugins/kimchi/ui/pages/network.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/report-add.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/report-rename.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/repository-add.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/repository-edit.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/storage.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/storagepool-add-volume.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/storagepool-add.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/template-add.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/template-edit.html.tmpl
delete mode 100644 plugins/kimchi/ui/pages/templates.html.tmpl
delete mode 100644 plugins/kimchi/ui/robots.txt
delete mode 100644 plugins/kimchi/ui/spice-html5/Makefile.am
delete mode 100644 plugins/kimchi/ui/spice-html5/atKeynames.js
delete mode 100644 plugins/kimchi/ui/spice-html5/bitmap.js
delete mode 100644 plugins/kimchi/ui/spice-html5/css/Makefile.am
delete mode 100644 plugins/kimchi/ui/spice-html5/css/spice.css
delete mode 100644 plugins/kimchi/ui/spice-html5/cursor.js
delete mode 100644 plugins/kimchi/ui/spice-html5/display.js
delete mode 100644 plugins/kimchi/ui/spice-html5/enums.js
delete mode 100644 plugins/kimchi/ui/spice-html5/inputs.js
delete mode 100644 plugins/kimchi/ui/spice-html5/lz.js
delete mode 100644 plugins/kimchi/ui/spice-html5/main.js
delete mode 100644 plugins/kimchi/ui/spice-html5/pages/Makefile.am
delete mode 100644 plugins/kimchi/ui/spice-html5/pages/spice_auto.html
delete mode 100644 plugins/kimchi/ui/spice-html5/playback.js
delete mode 100644 plugins/kimchi/ui/spice-html5/png.js
delete mode 100644 plugins/kimchi/ui/spice-html5/quic.js
delete mode 100644 plugins/kimchi/ui/spice-html5/resize.js
delete mode 100644 plugins/kimchi/ui/spice-html5/simulatecursor.js
delete mode 100644 plugins/kimchi/ui/spice-html5/spicearraybuffer.js
delete mode 100644 plugins/kimchi/ui/spice-html5/spiceconn.js
delete mode 100644 plugins/kimchi/ui/spice-html5/spicedataview.js
delete mode 100644 plugins/kimchi/ui/spice-html5/spicemsg.js
delete mode 100644 plugins/kimchi/ui/spice-html5/spicetype.js
delete mode 100644 plugins/kimchi/ui/spice-html5/thirdparty/Makefile.am
delete mode 100644 plugins/kimchi/ui/spice-html5/thirdparty/jsbn.js
delete mode 100644 plugins/kimchi/ui/spice-html5/thirdparty/prng4.js
delete mode 100644 plugins/kimchi/ui/spice-html5/thirdparty/rng.js
delete mode 100644 plugins/kimchi/ui/spice-html5/thirdparty/rsa.js
delete mode 100644 plugins/kimchi/ui/spice-html5/thirdparty/sha1.js
delete mode 100644 plugins/kimchi/ui/spice-html5/ticket.js
delete mode 100644 plugins/kimchi/ui/spice-html5/utils.js
delete mode 100644 plugins/kimchi/ui/spice-html5/webm.js
delete mode 100644 plugins/kimchi/ui/spice-html5/wire.js
delete mode 100644 plugins/kimchi/utils.py
delete mode 100644 plugins/kimchi/vmtemplate.py
delete mode 100644 plugins/kimchi/vnc.py
delete mode 100644 plugins/kimchi/xmlutils/Makefile.am
delete mode 100644 plugins/kimchi/xmlutils/__init__.py
delete mode 100644 plugins/kimchi/xmlutils/cpu.py
delete mode 100644 plugins/kimchi/xmlutils/disk.py
delete mode 100644 plugins/kimchi/xmlutils/graphics.py
delete mode 100644 plugins/kimchi/xmlutils/interface.py
delete mode 100644 plugins/kimchi/xmlutils/network.py
delete mode 100644 plugins/kimchi/xmlutils/qemucmdline.py
delete mode 100644 plugins/kimchi/yumparser.py
delete mode 100644 plugins/sample/API.json
delete mode 100644 plugins/sample/Makefile.am
delete mode 100644 plugins/sample/__init__.py
delete mode 120000 plugins/sample/config.status
delete mode 100644 plugins/sample/i18n.py
delete mode 100644 plugins/sample/model.py
delete mode 100644 plugins/sample/po/LINGUAS
delete mode 100644 plugins/sample/po/Makefile.in.in
delete mode 100644 plugins/sample/po/Makevars
delete mode 100644 plugins/sample/po/POTFILES.in
delete mode 100644 plugins/sample/po/en_US.po
delete mode 100755 plugins/sample/po/gen-pot
delete mode 100644 plugins/sample/po/pt_BR.po
delete mode 100644 plugins/sample/po/sample.pot
delete mode 100644 plugins/sample/po/zh_CN.po
delete mode 100644 plugins/sample/sample.conf.in
delete mode 100644 plugins/sample/ui/Makefile.am
delete mode 100644 plugins/sample/ui/config/Makefile.am
delete mode 100644 plugins/sample/ui/config/tab-ext.xml
delete mode 100644 plugins/sample/ui/css/.gitignore
delete mode 100644 plugins/sample/ui/images/.gitignore
delete mode 100644 plugins/sample/ui/js/.gitignore
delete mode 100644 plugins/sample/ui/js/Makefile.am
delete mode 100644 plugins/sample/ui/js/util.js
delete mode 100644 plugins/sample/ui/libs/.gitignore
delete mode 100644 plugins/sample/ui/pages/Makefile.am
delete mode 100644 plugins/sample/ui/pages/help/en_US/sample-tab1.html
delete mode 100644 plugins/sample/ui/pages/help/en_US/sample-tab2.html
delete mode 100644 plugins/sample/ui/pages/i18n.json.tmpl
delete mode 100644 plugins/sample/ui/pages/sample-tab1.html.tmpl
delete mode 100644 plugins/sample/ui/pages/sample-tab2.html.tmpl
create mode 100644 src/wok/plugins/Makefile.am
create mode 100644 src/wok/plugins/__init__.py
create mode 100644 src/wok/plugins/kimchi/.gitignore
create mode 100644 src/wok/plugins/kimchi/API.json
create mode 100644 src/wok/plugins/kimchi/CONTRIBUTE.md
create mode 100644 src/wok/plugins/kimchi/COPYING
create mode 100644 src/wok/plugins/kimchi/COPYING.ASL2
create mode 100644 src/wok/plugins/kimchi/COPYING.LGPL
create mode 100644 src/wok/plugins/kimchi/ChangeLog
create mode 100644 src/wok/plugins/kimchi/INSTALL
create mode 100644 src/wok/plugins/kimchi/Makefile.am
create mode 120000 src/wok/plugins/kimchi/README.md
create mode 100644 src/wok/plugins/kimchi/VERSION
create mode 100644 src/wok/plugins/kimchi/__init__.py
create mode 100755 src/wok/plugins/kimchi/autogen.sh
create mode 100644 src/wok/plugins/kimchi/build-aux/config.rpath
create mode 100755 src/wok/plugins/kimchi/build-aux/genChangelog
create mode 100755 src/wok/plugins/kimchi/build-aux/pkg-version
create mode 100644 src/wok/plugins/kimchi/config.py.in
create mode 100644 src/wok/plugins/kimchi/config.rpath
create mode 100644 src/wok/plugins/kimchi/configure.ac
create mode 100644 src/wok/plugins/kimchi/contrib/DEBIAN/Makefile.am
create mode 100644 src/wok/plugins/kimchi/contrib/DEBIAN/control.in
create mode 100644 src/wok/plugins/kimchi/contrib/Makefile.am
create mode 100755 src/wok/plugins/kimchi/contrib/check_i18n.py
create mode 100644 src/wok/plugins/kimchi/contrib/kimchi.spec.fedora.in
create mode 100644 src/wok/plugins/kimchi/contrib/kimchi.spec.suse.in
create mode 100644 src/wok/plugins/kimchi/contrib/make-deb.sh.in
create mode 100644 src/wok/plugins/kimchi/control/Makefile.am
create mode 100644 src/wok/plugins/kimchi/control/__init__.py
create mode 100644 src/wok/plugins/kimchi/control/config.py
create mode 100644 src/wok/plugins/kimchi/control/cpuinfo.py
create mode 100644 src/wok/plugins/kimchi/control/debugreports.py
create mode 100644 src/wok/plugins/kimchi/control/groups.py
create mode 100644 src/wok/plugins/kimchi/control/host.py
create mode 100644 src/wok/plugins/kimchi/control/interfaces.py
create mode 100644 src/wok/plugins/kimchi/control/networks.py
create mode 100644 src/wok/plugins/kimchi/control/peers.py
create mode 100644 src/wok/plugins/kimchi/control/storagepools.py
create mode 100644 src/wok/plugins/kimchi/control/storageservers.py
create mode 100644 src/wok/plugins/kimchi/control/storagevolumes.py
create mode 100644 src/wok/plugins/kimchi/control/templates.py
create mode 100644 src/wok/plugins/kimchi/control/users.py
create mode 100644 src/wok/plugins/kimchi/control/vm/Makefile.am
create mode 100644 src/wok/plugins/kimchi/control/vm/__init__.py
create mode 100644 src/wok/plugins/kimchi/control/vm/hostdevs.py
create mode 100644 src/wok/plugins/kimchi/control/vm/ifaces.py
create mode 100644 src/wok/plugins/kimchi/control/vm/snapshots.py
create mode 100644 src/wok/plugins/kimchi/control/vm/storages.py
create mode 100644 src/wok/plugins/kimchi/control/vms.py
create mode 100644 src/wok/plugins/kimchi/disks.py
create mode 100644 src/wok/plugins/kimchi/distroloader.py
create mode 100644 src/wok/plugins/kimchi/distros.d/Makefile.am
create mode 100644 src/wok/plugins/kimchi/distros.d/debian.json
create mode 100644 src/wok/plugins/kimchi/distros.d/fedora.json
create mode 100644 src/wok/plugins/kimchi/distros.d/gentoo.json
create mode 100644 src/wok/plugins/kimchi/distros.d/opensuse.json
create mode 100644 src/wok/plugins/kimchi/distros.d/ubuntu.json
create mode 100644 src/wok/plugins/kimchi/docs/API.md
create mode 100644 src/wok/plugins/kimchi/docs/Makefile.am
create mode 100644 src/wok/plugins/kimchi/docs/README-federation.md
create mode 100644 src/wok/plugins/kimchi/docs/README.md
create mode 100644 src/wok/plugins/kimchi/docs/kimchi-guest.png
create mode 100644 src/wok/plugins/kimchi/docs/kimchi-login.png
create mode 100644 src/wok/plugins/kimchi/docs/kimchi-templates.png
create mode 100644 src/wok/plugins/kimchi/i18n.py
create mode 100644 src/wok/plugins/kimchi/imageinfo.py
create mode 100644 src/wok/plugins/kimchi/iscsi.py
create mode 100644 src/wok/plugins/kimchi/isoinfo.py
create mode 100644 src/wok/plugins/kimchi/kimchi.conf
create mode 100644 src/wok/plugins/kimchi/kvmusertests.py
create mode 100644 src/wok/plugins/kimchi/m4/ac_python_module.m4
create mode 100644 src/wok/plugins/kimchi/m4/gettext.m4
create mode 100644 src/wok/plugins/kimchi/m4/iconv.m4
create mode 100644 src/wok/plugins/kimchi/m4/intlmacosx.m4
create mode 100644 src/wok/plugins/kimchi/m4/lib-ld.m4
create mode 100644 src/wok/plugins/kimchi/m4/lib-link.m4
create mode 100644 src/wok/plugins/kimchi/m4/lib-prefix.m4
create mode 100644 src/wok/plugins/kimchi/m4/nls.m4
create mode 100644 src/wok/plugins/kimchi/m4/po.m4
create mode 100644 src/wok/plugins/kimchi/m4/progtest.m4
create mode 100644 src/wok/plugins/kimchi/mockmodel.py
create mode 100644 src/wok/plugins/kimchi/model/Makefile.am
create mode 100644 src/wok/plugins/kimchi/model/__init__.py
create mode 100644 src/wok/plugins/kimchi/model/config.py
create mode 100644 src/wok/plugins/kimchi/model/cpuinfo.py
create mode 100644 src/wok/plugins/kimchi/model/debugreports.py
create mode 100644 src/wok/plugins/kimchi/model/diskutils.py
create mode 100644 src/wok/plugins/kimchi/model/featuretests.py
create mode 100644 src/wok/plugins/kimchi/model/groups.py
create mode 100644 src/wok/plugins/kimchi/model/host.py
create mode 100644 src/wok/plugins/kimchi/model/hostdev.py
create mode 100644 src/wok/plugins/kimchi/model/interfaces.py
create mode 100644 src/wok/plugins/kimchi/model/libvirtconnection.py
create mode 100644 src/wok/plugins/kimchi/model/libvirtstoragepool.py
create mode 100644 src/wok/plugins/kimchi/model/model.py
create mode 100644 src/wok/plugins/kimchi/model/networks.py
create mode 100644 src/wok/plugins/kimchi/model/peers.py
create mode 100644 src/wok/plugins/kimchi/model/storagepools.py
create mode 100644 src/wok/plugins/kimchi/model/storageservers.py
create mode 100644 src/wok/plugins/kimchi/model/storagetargets.py
create mode 100644 src/wok/plugins/kimchi/model/storagevolumes.py
create mode 100644 src/wok/plugins/kimchi/model/templates.py
create mode 100644 src/wok/plugins/kimchi/model/users.py
create mode 100644 src/wok/plugins/kimchi/model/utils.py
create mode 100644 src/wok/plugins/kimchi/model/vmhostdevs.py
create mode 100644 src/wok/plugins/kimchi/model/vmifaces.py
create mode 100644 src/wok/plugins/kimchi/model/vms.py
create mode 100644 src/wok/plugins/kimchi/model/vmsnapshots.py
create mode 100644 src/wok/plugins/kimchi/model/vmstorages.py
create mode 100644 src/wok/plugins/kimchi/netinfo.py
create mode 100644 src/wok/plugins/kimchi/network.py
create mode 100644 src/wok/plugins/kimchi/osinfo.py
create mode 100644 src/wok/plugins/kimchi/po/LINGUAS
create mode 100644 src/wok/plugins/kimchi/po/Makefile.in.in
create mode 100644 src/wok/plugins/kimchi/po/Makevars
create mode 100644 src/wok/plugins/kimchi/po/POTFILES.in
create mode 100644 src/wok/plugins/kimchi/po/de_DE.po
create mode 100644 src/wok/plugins/kimchi/po/en_US.po
create mode 100644 src/wok/plugins/kimchi/po/es_ES.po
create mode 100644 src/wok/plugins/kimchi/po/fr_FR.po
create mode 100644 src/wok/plugins/kimchi/po/gen-pot.in
create mode 100644 src/wok/plugins/kimchi/po/it_IT.po
create mode 100644 src/wok/plugins/kimchi/po/ja_JP.po
create mode 100755 src/wok/plugins/kimchi/po/kimchi.pot
create mode 100644 src/wok/plugins/kimchi/po/ko_KR.po
create mode 100644 src/wok/plugins/kimchi/po/pt_BR.po
create mode 100644 src/wok/plugins/kimchi/po/ru_RU.po
create mode 100644 src/wok/plugins/kimchi/po/zh_CN.po
create mode 100644 src/wok/plugins/kimchi/po/zh_TW.po
create mode 100644 src/wok/plugins/kimchi/repositories.py
create mode 100644 src/wok/plugins/kimchi/root.py
create mode 100644 src/wok/plugins/kimchi/scan.py
create mode 100644 src/wok/plugins/kimchi/screenshot.py
create mode 100644 src/wok/plugins/kimchi/swupdate.py
create mode 100644 src/wok/plugins/kimchi/template.conf
create mode 100644 src/wok/plugins/kimchi/tests/Makefile.am
create mode 100644 src/wok/plugins/kimchi/tests/iso_gen.py
create mode 100644 src/wok/plugins/kimchi/tests/run_tests.sh.in
create mode 100644 src/wok/plugins/kimchi/tests/test_authorization.py
create mode 100644 src/wok/plugins/kimchi/tests/test_config.py.in
create mode 100644 src/wok/plugins/kimchi/tests/test_host.py
create mode 100644 src/wok/plugins/kimchi/tests/test_mock_network.py
create mode 100644 src/wok/plugins/kimchi/tests/test_mock_storagepool.py
create mode 100644 src/wok/plugins/kimchi/tests/test_mock_storagevolume.py
create mode 100644 src/wok/plugins/kimchi/tests/test_mockmodel.py
create mode 100644 src/wok/plugins/kimchi/tests/test_model.py
create mode 100644 src/wok/plugins/kimchi/tests/test_model_network.py
create mode 100644 src/wok/plugins/kimchi/tests/test_model_storagepool.py
create mode 100644 src/wok/plugins/kimchi/tests/test_model_storagevolume.py
create mode 100644 src/wok/plugins/kimchi/tests/test_networkxml.py
create mode 100644 src/wok/plugins/kimchi/tests/test_osinfo.py
create mode 100644 src/wok/plugins/kimchi/tests/test_rest.py
create mode 100644 src/wok/plugins/kimchi/tests/test_storagepoolxml.py
create mode 100644 src/wok/plugins/kimchi/tests/test_template.py
create mode 100644 src/wok/plugins/kimchi/tests/test_vmtemplate.py
create mode 100644 src/wok/plugins/kimchi/tests/test_yumparser.py
create mode 100644 src/wok/plugins/kimchi/tests/utils.py
create mode 100644 src/wok/plugins/kimchi/ui/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/config/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/config/tab-ext.xml
create mode 100644 src/wok/plugins/kimchi/ui/css/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/guest-edit.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/guest-storage-add.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/host.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/icon.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/list.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/network.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/report-add.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/report-rename.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/repository-add.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/repository-edit.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/storage.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/storagepool-add-volume.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/template-edit.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/template.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/template_add.css
create mode 100644 src/wok/plugins/kimchi/ui/css/theme-default/template_list.css
create mode 100644 src/wok/plugins/kimchi/ui/images/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/images/icon-centos.png
create mode 100644 src/wok/plugins/kimchi/ui/images/icon-debian.png
create mode 100644 src/wok/plugins/kimchi/ui/images/icon-fedora.png
create mode 100644 src/wok/plugins/kimchi/ui/images/icon-gentoo.png
create mode 100644 src/wok/plugins/kimchi/ui/images/icon-opensuse.png
create mode 100644 src/wok/plugins/kimchi/ui/images/icon-ubuntu.png
create mode 100644 src/wok/plugins/kimchi/ui/images/icon-vm.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/ac22_pause.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/ac22_pause_grey.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/ac24_resume.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/ac24_resume_grey.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/arrow-down-black.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/arrow-down-disable.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/arrow-down.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/arrow-up.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/arrow_out.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/group.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/host-icon-sprite.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-back.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-camera.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-design.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-detail.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-iso.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-list.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-load.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-local.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-power-down.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-power-up.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-qcow2.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-raw.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-remote.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-reset.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-search.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-sort.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-tree.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-user.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/icon-volume-default.png
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/kimchi-loading15x15.gif
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/loading.gif
create mode 100644 src/wok/plugins/kimchi/ui/images/theme-default/user.png
create mode 100644 src/wok/plugins/kimchi/ui/js/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.api.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.guest_add_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.guest_edit_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.guest_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.guest_media_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.guest_storage_add.main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.host.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.network.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.report_add_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.report_rename_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.repository_add_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.repository_edit_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.storage_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.storagepool_add_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.storagepool_add_volume_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.template_add_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.template_edit_main.js
create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.template_main.js
create mode 100644 src/wok/plugins/kimchi/ui/pages/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/guest-add.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/guest-edit.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/guest-storage-add.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/guest.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/guests.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/de_DE/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/de_DE/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/de_DE/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/de_DE/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/de_DE/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/de_DE/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/dita-help.xsl
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/en_US/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/en_US/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/en_US/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/en_US/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/en_US/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/en_US/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/es_ES/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/es_ES/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/es_ES/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/es_ES/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/es_ES/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/es_ES/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/fr_FR/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/fr_FR/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/fr_FR/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/fr_FR/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/fr_FR/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/fr_FR/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/it_IT/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/it_IT/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/it_IT/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/it_IT/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/it_IT/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/it_IT/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ja_JP/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ja_JP/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ja_JP/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ja_JP/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ja_JP/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ja_JP/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/kimchi.css
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ko_KR/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ko_KR/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ko_KR/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ko_KR/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ko_KR/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ko_KR/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/pt_BR/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/pt_BR/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/pt_BR/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/pt_BR/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/pt_BR/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/pt_BR/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ru_RU/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ru_RU/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ru_RU/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ru_RU/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ru_RU/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/ru_RU/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_CN/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_CN/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_CN/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_CN/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_CN/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_CN/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_TW/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_TW/guests.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_TW/host.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_TW/network.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_TW/storage.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/help/zh_TW/templates.dita
create mode 100644 src/wok/plugins/kimchi/ui/pages/host.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/i18n.json.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/network.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/report-add.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/report-rename.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/repository-add.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/repository-edit.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/storage.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/storagepool-add-volume.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/storagepool-add.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/template-add.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/template-edit.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/pages/templates.html.tmpl
create mode 100644 src/wok/plugins/kimchi/ui/robots.txt
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/atKeynames.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/bitmap.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/css/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/css/spice.css
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/cursor.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/display.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/enums.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/inputs.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/lz.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/main.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/pages/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/pages/spice_auto.html
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/playback.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/png.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/quic.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/resize.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/simulatecursor.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/spicearraybuffer.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/spiceconn.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/spicedataview.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/spicemsg.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/spicetype.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/thirdparty/Makefile.am
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/thirdparty/jsbn.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/thirdparty/prng4.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/thirdparty/rng.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/thirdparty/rsa.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/thirdparty/sha1.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/ticket.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/utils.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/webm.js
create mode 100644 src/wok/plugins/kimchi/ui/spice-html5/wire.js
create mode 100644 src/wok/plugins/kimchi/utils.py
create mode 100644 src/wok/plugins/kimchi/vmtemplate.py
create mode 100644 src/wok/plugins/kimchi/vnc.py
create mode 100644 src/wok/plugins/kimchi/xmlutils/Makefile.am
create mode 100644 src/wok/plugins/kimchi/xmlutils/__init__.py
create mode 100644 src/wok/plugins/kimchi/xmlutils/cpu.py
create mode 100644 src/wok/plugins/kimchi/xmlutils/disk.py
create mode 100644 src/wok/plugins/kimchi/xmlutils/graphics.py
create mode 100644 src/wok/plugins/kimchi/xmlutils/interface.py
create mode 100644 src/wok/plugins/kimchi/xmlutils/network.py
create mode 100644 src/wok/plugins/kimchi/xmlutils/qemucmdline.py
create mode 100644 src/wok/plugins/kimchi/yumparser.py
create mode 100644 src/wok/plugins/sample/API.json
create mode 100644 src/wok/plugins/sample/Makefile.am
create mode 100644 src/wok/plugins/sample/__init__.py
create mode 120000 src/wok/plugins/sample/config.status
create mode 100644 src/wok/plugins/sample/i18n.py
create mode 100644 src/wok/plugins/sample/model.py
create mode 100644 src/wok/plugins/sample/po/LINGUAS
create mode 100644 src/wok/plugins/sample/po/Makefile.in.in
create mode 100644 src/wok/plugins/sample/po/Makevars
create mode 100644 src/wok/plugins/sample/po/POTFILES.in
create mode 100644 src/wok/plugins/sample/po/en_US.po
create mode 100755 src/wok/plugins/sample/po/gen-pot
create mode 100644 src/wok/plugins/sample/po/pt_BR.po
create mode 100644 src/wok/plugins/sample/po/sample.pot
create mode 100644 src/wok/plugins/sample/po/zh_CN.po
create mode 100644 src/wok/plugins/sample/sample.conf.in
create mode 100644 src/wok/plugins/sample/ui/Makefile.am
create mode 100644 src/wok/plugins/sample/ui/config/Makefile.am
create mode 100644 src/wok/plugins/sample/ui/config/tab-ext.xml
create mode 100644 src/wok/plugins/sample/ui/css/.gitignore
create mode 100644 src/wok/plugins/sample/ui/images/.gitignore
create mode 100644 src/wok/plugins/sample/ui/js/.gitignore
create mode 100644 src/wok/plugins/sample/ui/js/Makefile.am
create mode 100644 src/wok/plugins/sample/ui/js/util.js
create mode 100644 src/wok/plugins/sample/ui/libs/.gitignore
create mode 100644 src/wok/plugins/sample/ui/pages/Makefile.am
create mode 100644 src/wok/plugins/sample/ui/pages/help/en_US/sample-tab1.html
create mode 100644 src/wok/plugins/sample/ui/pages/help/en_US/sample-tab2.html
create mode 100644 src/wok/plugins/sample/ui/pages/i18n.json.tmpl
create mode 100644 src/wok/plugins/sample/ui/pages/sample-tab1.html.tmpl
create mode 100644 src/wok/plugins/sample/ui/pages/sample-tab2.html.tmpl
--
2.4.3
9 years, 2 months
[PATCH] Issue #704: page refreshing in Wok when no plugins installed
by pvital@linux.vnet.ibm.com
From: Atreyee <atreyee(a)linux.vnet.ibm.com>
This patch fixes the issue of continuous page refresh once we logged into
wok running without any plugin installed.
Page url was switching between '/#' and '/undefined' . With the existing
UI code, a infinite loop got created for loading the page when no plugin
installed.
Signed-off-by: Atreyee <atreyee(a)linux.vnet.ibm.com>
---
ui/css/theme-default/message.css | 10 ++++++++++
ui/js/src/wok.main.js | 15 +++++++++++++--
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/ui/css/theme-default/message.css b/ui/css/theme-default/message.css
index c3f9b03..e77886b 100644
--- a/ui/css/theme-default/message.css
+++ b/ui/css/theme-default/message.css
@@ -133,3 +133,13 @@
border: 2px solid #444;
color: #444;
}
+
+.noPluginMessage{
+ font-size: 18px;
+ height: 48px;
+ line-height: 48px;
+ text-shadow: -1px -1px 1px #ccc, 1px 1px 1px #fff;
+ padding-left: 10px;
+ padding-top:10px;
+
+}
diff --git a/ui/js/src/wok.main.js b/ui/js/src/wok.main.js
index 570743d..447140e 100644
--- a/ui/js/src/wok.main.js
+++ b/ui/js/src/wok.main.js
@@ -102,10 +102,15 @@ wok.main = function() {
var defaultTab = tabs[0]
var defaultTabPath = defaultTab && defaultTab['path']
+
+ //redirect to empty page when no plugin installed
+ if(tabs.length===0){
+ DEFAULT_HASH = 'wok-empty';
+ }else{
// Remove file extension from 'defaultTabPath'
DEFAULT_HASH = defaultTabPath &&
defaultTabPath.substring(0, defaultTabPath.lastIndexOf('.'))
-
+ }
$('#nav-menu').append(genTabs(tabs));
callback && callback();
@@ -136,11 +141,16 @@ wok.main = function() {
* and clear location.hash to jump to home page.
*/
var tab = $('#nav-menu a[href="' + url + '"]');
- if (tab.length === 0) {
+ if (tab.length === 0 && url!='wok-empty.html') {
location.hash = '';
return;
}
+ //Remove the tab arrow indicator for no plugin
+ if(url=='wok-empty.html'){
+ $('.menu-arrow').hide();
+ $('#main').html('No plugins installed currently.You can download the available plugins <a href="https://github.com/kimchi-project/kimchi">Kimchi</a> and <a href="https://github.com/kimchi-project/ginger">Ginger</a> from Github').addClass('noPluginMessage');
+ }else{
// Animate arrow indicator.
var left = $(tab).parent().position().left;
var width = $(tab).parent().width();
@@ -163,6 +173,7 @@ wok.main = function() {
}
// Load page content.
loadPage(url);
+ }
};
/**
--
2.4.3
9 years, 2 months
[PATCH 0/3] [issue #731] Raw volumes validation
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)linux.vnet.ibm.com>
Kimchi allows to attach Raw volumes that are not disk images when editing
a VM. With this, it's possible attach a XML or PDF file (that qemu-img and
libvirt see as raw volumes) as a disk into the guest.
This patch-set introduces a check for all 'raw' volumes to see if them are
valid disks or not (by using libmagic), and then, pass this information to
the front-end to make a correct list of volumes option when attaching a new
one into a VM.
Paulo Vital (3):
Raw volumes validation: update contrib and README
Raw volumes validation: back-end and front-end
Raw volumes validation: update tests
contrib/DEBIAN/control.in | 3 ++-
contrib/kimchi.spec.fedora.in | 1 +
contrib/kimchi.spec.suse.in | 1 +
docs/API.md | 1 +
docs/README.md | 11 +++++------
src/kimchi/control/storagevolumes.py | 3 ++-
src/kimchi/mockmodel.py | 6 ++++--
src/kimchi/model/storagevolumes.py | 15 ++++++++++++++-
tests/test_model_storagevolume.py | 2 +-
ui/js/src/kimchi.guest_storage_add.main.js | 2 +-
10 files changed, 32 insertions(+), 13 deletions(-)
--
2.4.3
9 years, 2 months
[PATCH 0/2] Issue 557 - Package Update Improvements - Part I
by Jose Ricardo Ziviani
This patch avoid any package update interruption that could be caused by
kimchid service. This is important because if kimchid is stopped or
restarted during a package update process, the system can become
unstable.
A part II is being implemented to let kimchi knows whether a package
update is happening when kimchid is (re)started.
Jose Ricardo Ziviani (2):
Make the package update process indendent of Kimchi
Set systemd KillMode to process
contrib/wokd.service.fedora | 1 +
plugins/kimchi/swupdate.py | 13 ++++++++++++-
2 files changed, 13 insertions(+), 1 deletion(-)
--
1.9.1
9 years, 2 months
Re-licensing Wok/Kimchi to LGPLv2
by Aline Manera
Hi all,
Kimchi was initially created under LGPLv2, but after importing LGPLv3
code (websockify) to Kimchi source code, it was re-licensed to LGPLv3
(the current license).
As Kimchi doesn't import any LGPLv3 code any more, I am wondering what
do you think in re-licensing it back to LGPLv2.
So Wok and Kimchi would have the same licenses: backend under LGPLv2 and
frontend under Apache v2.
Please, let me know your thoughts on it.
Regards,
Aline Manera
9 years, 2 months
[PATCH v4 0/5] Improve VM CPU update code
by Jose Ricardo Ziviani
v4:
- merged with "Improve VCPU code" patch
- grouped similar commits together
v3:
- included commit "Hot add/remove CPUs on PPC"
v2:
- patch rebased
This patchset refactors part of the code related to updating VM CPUs.
I am aware of two tests which are failing with this patchset. They'll be worked
on before the final version.
Crístian Deives (3):
Use locks to prevent concurrent updates to VMs
Update VCPU by using libvirt function
Check if the VM update params are valid for the current state
Crístian Viana (1):
Hot add/remove CPUs on PPC
Jose Ricardo Ziviani (1):
Forbid user to edit CPU value if topology is defined
src/kimchi/i18n.py | 6 ++
src/kimchi/mockmodel.py | 48 +++++-------
src/kimchi/model/vms.py | 198 +++++++++++++++++++++++++++++++++++++++++++++---
tests/test_rest.py | 3 +-
4 files changed, 215 insertions(+), 40 deletions(-)
--
1.9.1
9 years, 2 months
How to build Wok/Kimchi
by Aline Manera
Hi all,
Due the separation between Wok and Kimchi as a plugin the build process
changed a little bit.
We need to build the plugin (Kimchi) and the framework (wok).
Please, follow the steps below:
# To build Kimchi
# You need to repeat those steps for each plugin your have in your
source code
cd plugins/kimchi
sudo ./autogen.sh --system && sudo make
# To build Wok
cd ../../
sudo ./autogen.sh --system && sudo make
After that you can start the wokd service:
sudo src/wrokd
Regards,
Aline Manera
9 years, 2 months