[PATCH V2] Fix Kimchi model

With the Kimchi/Wok split, the Tasks model is now outside of Kimchi's model tree and needs to be imported separately. This patch does that and creates function get_instances() to avoid code duplication. It also fixes related documentation and URIs to use /plugins/kimchi/tasks, which is now exported by Kimchi. Signed-off-by: Lucio Correia <luciojhc@linux.vnet.ibm.com> --- src/wok/plugins/kimchi/docs/API.md | 33 ++++++++++++++++++++++ src/wok/plugins/kimchi/model/model.py | 28 +++++++++++++----- src/wok/plugins/kimchi/tests/test_host.py | 4 +-- .../plugins/kimchi/tests/test_mock_storagepool.py | 2 +- .../kimchi/tests/test_model_storagevolume.py | 8 +++--- src/wok/plugins/kimchi/tests/test_rest.py | 24 ++++++++-------- src/wok/plugins/kimchi/ui/js/src/kimchi.api.js | 4 +-- 7 files changed, 75 insertions(+), 28 deletions(-) diff --git a/src/wok/plugins/kimchi/docs/API.md b/src/wok/plugins/kimchi/docs/API.md index ccc843f..e7f399b 100644 --- a/src/wok/plugins/kimchi/docs/API.md +++ b/src/wok/plugins/kimchi/docs/API.md @@ -35,6 +35,39 @@ the following general conventions: * Variable segments in the URI begin with a ':' and should replaced with the appropriate resource identifier. + +### Collection: Tasks + +**URI:** /plugins/kimchi/tasks + +**Methods:** + +* **GET**: Retrieve a summarized list of current Kimchi specific Tasks (stored +in Kimchi's object store) + +### Resource: Task + +**URI:** /plugins/kimchi/tasks/*:id* + +A task represents an asynchronous operation that is being performed by the +server. + +**Methods:** + +* **GET**: Retrieve the full description of the Task + * id: The Task ID is used to identify this Task in the API. + * status: The current status of the Task + * running: The task is running + * finished: The task has finished successfully + * failed: The task failed + * message: Human-readable details about the Task status + * target_uri: Resource URI related to the Task +* **POST**: *See Task Actions* + +**Actions (POST):** + +*No actions defined* + ### Collection: Virtual Machines **URI:** /plugins/kimchi/vms diff --git a/src/wok/plugins/kimchi/model/model.py b/src/wok/plugins/kimchi/model/model.py index 0c94f63..39097c4 100644 --- a/src/wok/plugins/kimchi/model/model.py +++ b/src/wok/plugins/kimchi/model/model.py @@ -30,23 +30,37 @@ from libvirtconnection import LibvirtConnection class Model(BaseModel): def __init__(self, libvirt_uri=None, objstore_loc=None): + def get_instances(module_name): + instances = [] + module = import_module(module_name) + members = inspect.getmembers(module, inspect.isclass) + for cls_name, instance in members: + if inspect.getmodule(instance) == module and \ + cls_name.endswith('Model'): + instances.append(instance) + + return instances + self.objstore = ObjectStore(objstore_loc) self.conn = LibvirtConnection(libvirt_uri) kargs = {'objstore': self.objstore, 'conn': self.conn} + models = [] + # Import task model from Wok + instances = get_instances('wok.model.tasks') + for instance in instances: + models.append(instance(**kargs)) + + # Import all Kimchi plugin models this = os.path.basename(__file__) this_mod = os.path.splitext(this)[0] - models = [] for mod_name in listPathModules(os.path.dirname(__file__)): if mod_name.startswith("_") or mod_name == this_mod: continue - module = import_module('plugins.kimchi.model.' + mod_name) - members = inspect.getmembers(module, inspect.isclass) - for cls_name, instance in members: - if inspect.getmodule(instance) == module: - if cls_name.endswith('Model'): - models.append(instance(**kargs)) + instances = get_instances('plugins.kimchi.model.' + mod_name) + for instance in instances: + models.append(instance(**kargs)) return super(Model, self).__init__(models) diff --git a/src/wok/plugins/kimchi/tests/test_host.py b/src/wok/plugins/kimchi/tests/test_host.py index f3da49b..2940f48 100644 --- a/src/wok/plugins/kimchi/tests/test_host.py +++ b/src/wok/plugins/kimchi/tests/test_host.py @@ -128,12 +128,12 @@ class HostTests(unittest.TestCase): task_params = [u'id', u'message', u'status', u'target_uri'] self.assertEquals(sorted(task_params), sorted(task.keys())) - resp = self.request('/tasks/' + task[u'id'], None, + resp = self.request('/plugins/kimchi/tasks/' + task[u'id'], None, 'GET') task_info = json.loads(resp.read()) self.assertEquals(task_info['status'], 'running') wait_task(_task_lookup, task_info['id']) - resp = self.request('/tasks/' + task[u'id'], None, + resp = self.request('/plugins/kimchi/tasks/' + task[u'id'], None, 'GET') task_info = json.loads(resp.read()) self.assertEquals(task_info['status'], 'finished') diff --git a/src/wok/plugins/kimchi/tests/test_mock_storagepool.py b/src/wok/plugins/kimchi/tests/test_mock_storagepool.py index 5cf5b3e..ea9843b 100644 --- a/src/wok/plugins/kimchi/tests/test_mock_storagepool.py +++ b/src/wok/plugins/kimchi/tests/test_mock_storagepool.py @@ -61,7 +61,7 @@ class MockStoragepoolTests(unittest.TestCase): def _task_lookup(self, taskid): return json.loads( - self.request('/tasks/%s' % taskid).read() + self.request('/plugins/kimchi/tasks/%s' % taskid).read() ) def test_storagepool(self): diff --git a/src/wok/plugins/kimchi/tests/test_model_storagevolume.py b/src/wok/plugins/kimchi/tests/test_model_storagevolume.py index 8812e46..087dd7b 100644 --- a/src/wok/plugins/kimchi/tests/test_model_storagevolume.py +++ b/src/wok/plugins/kimchi/tests/test_model_storagevolume.py @@ -64,7 +64,7 @@ def tearDownModule(): def _do_volume_test(self, model, host, ssl_port, pool_name): def _task_lookup(taskid): return json.loads( - self.request('/tasks/%s' % taskid).read() + self.request('/plugins/kimchi/tasks/%s' % taskid).read() ) uri = '/plugins/kimchi/storagepools/%s/storagevolumes' \ @@ -91,7 +91,7 @@ def _do_volume_test(self, model, host, ssl_port, pool_name): task_id = json.loads(resp.read())['id'] wait_task(_task_lookup, task_id) status = json.loads( - self.request('/tasks/%s' % task_id).read() + self.request('/plugins/kimchi/tasks/%s' % task_id).read() ) self.assertEquals('finished', status['status']) vol_info = json.loads(self.request(vol_uri).read()) @@ -137,7 +137,7 @@ def _do_volume_test(self, model, host, ssl_port, pool_name): cloned_vol_name) wait_task(_task_lookup, task['id']) task = json.loads( - self.request('/tasks/%s' % task['id']).read() + self.request('/plugins/kimchi/tasks/%s' % task['id']).read() ) self.assertEquals('finished', task['status']) resp = self.request(uri + '/' + cloned_vol_name.encode('utf-8')) @@ -177,7 +177,7 @@ def _do_volume_test(self, model, host, ssl_port, pool_name): self.assertEquals(202, resp.status) task_id = json.loads(resp.read())['id'] wait_task(_task_lookup, task_id) - status = json.loads(self.request('/tasks/%s' % + status = json.loads(self.request('/plugins/kimchi/tasks/%s' % task_id).read()) self.assertEquals('ready for upload', status['message']) diff --git a/src/wok/plugins/kimchi/tests/test_rest.py b/src/wok/plugins/kimchi/tests/test_rest.py index 8cf4bd3..e1a2f54 100644 --- a/src/wok/plugins/kimchi/tests/test_rest.py +++ b/src/wok/plugins/kimchi/tests/test_rest.py @@ -335,7 +335,7 @@ class RestTests(unittest.TestCase): task = json.loads(resp.read()) wait_task(self._task_lookup, task['id']) task = json.loads( - self.request('/tasks/%s' % task['id'], '{}').read() + self.request('/plugins/kimchi/tasks/%s' % task['id'], '{}').read() ) self.assertEquals('finished', task['status']) clone_vm_name = task['target_uri'].split('/')[-2] @@ -366,7 +366,7 @@ class RestTests(unittest.TestCase): task = json.loads(resp.read()) wait_task(self._task_lookup, task['id']) task = json.loads( - self.request('/tasks/%s' % task['id']).read() + self.request('/plugins/kimchi/tasks/%s' % task['id']).read() ) self.assertEquals('finished', task['status']) @@ -404,7 +404,7 @@ class RestTests(unittest.TestCase): task = json.loads(resp.read()) snap_name = task['target_uri'].split('/')[-1] wait_task(self._task_lookup, task['id']) - resp = self.request('/tasks/%s' % task['id'], '{}', + resp = self.request('/plugins/kimchi/tasks/%s' % task['id'], '{}', 'GET') task = json.loads(resp.read()) self.assertEquals('finished', task['status']) @@ -1172,37 +1172,37 @@ class RestTests(unittest.TestCase): def _task_lookup(self, taskid): return json.loads( - self.request('/tasks/%s' % taskid).read() + self.request('/plugins/kimchi/tasks/%s' % taskid).read() ) def test_tasks(self): - id1 = add_task('/tasks/1', self._async_op, + id1 = add_task('/plugins/kimchi/tasks/1', self._async_op, model.objstore) - id2 = add_task('/tasks/2', self._except_op, + id2 = add_task('/plugins/kimchi/tasks/2', self._except_op, model.objstore) - id3 = add_task('/tasks/3', self._intermid_op, + id3 = add_task('/plugins/kimchi/tasks/3', self._intermid_op, model.objstore) - target_uri = urllib2.quote('^/tasks/*', safe="") + target_uri = urllib2.quote('^/plugins/kimchi/tasks/*', safe="") filter_data = 'status=running&target_uri=%s' % target_uri tasks = json.loads( - self.request('/tasks?%s' % filter_data).read() + self.request('/plugins/kimchi/tasks?%s' % filter_data).read() ) self.assertEquals(3, len(tasks)) - tasks = json.loads(self.request('/tasks').read()) + tasks = json.loads(self.request('/plugins/kimchi/tasks').read()) tasks_ids = [int(t['id']) for t in tasks] self.assertEquals(set([id1, id2, id3]) - set(tasks_ids), set([])) wait_task(self._task_lookup, id2) foo2 = json.loads( - self.request('/tasks/%s' % id2).read() + self.request('/plugins/kimchi/tasks/%s' % id2).read() ) keys = ['id', 'status', 'message', 'target_uri'] self.assertEquals(sorted(keys), sorted(foo2.keys())) self.assertEquals('failed', foo2['status']) wait_task(self._task_lookup, id3) foo3 = json.loads( - self.request('/tasks/%s' % id3).read() + self.request('/plugins/kimchi/tasks/%s' % id3).read() ) self.assertEquals('in progress', foo3['message']) self.assertEquals('running', foo3['status']) diff --git a/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js b/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js index a16c95e..bb3a53d 100644 --- a/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js +++ b/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js @@ -525,7 +525,7 @@ var kimchi = { getTask : function(taskId, suc, err) { wok.requestJSON({ - url : 'tasks/' + encodeURIComponent(taskId), + url : 'plugins/kimchi/tasks/' + encodeURIComponent(taskId), type : 'GET', contentType : 'application/json', dataType : 'json', @@ -536,7 +536,7 @@ var kimchi = { getTasksByFilter : function(filter, suc, err, sync) { wok.requestJSON({ - url : 'tasks?' + filter, + url : 'plugins/kimchi/tasks?' + filter, type : 'GET', contentType : 'application/json', dataType : 'json', -- 1.9.1

On 19/10/2015 14:45, Lucio Correia wrote:
With the Kimchi/Wok split, the Tasks model is now outside of Kimchi's model tree and needs to be imported separately. This patch does that and creates function get_instances() to avoid code duplication.
It also fixes related documentation and URIs to use /plugins/kimchi/tasks, which is now exported by Kimchi.
Signed-off-by: Lucio Correia <luciojhc@linux.vnet.ibm.com> --- src/wok/plugins/kimchi/docs/API.md | 33 ++++++++++++++++++++++ src/wok/plugins/kimchi/model/model.py | 28 +++++++++++++----- src/wok/plugins/kimchi/tests/test_host.py | 4 +-- .../plugins/kimchi/tests/test_mock_storagepool.py | 2 +- .../kimchi/tests/test_model_storagevolume.py | 8 +++--- src/wok/plugins/kimchi/tests/test_rest.py | 24 ++++++++-------- src/wok/plugins/kimchi/ui/js/src/kimchi.api.js | 4 +-- 7 files changed, 75 insertions(+), 28 deletions(-)
diff --git a/src/wok/plugins/kimchi/docs/API.md b/src/wok/plugins/kimchi/docs/API.md index ccc843f..e7f399b 100644 --- a/src/wok/plugins/kimchi/docs/API.md +++ b/src/wok/plugins/kimchi/docs/API.md @@ -35,6 +35,39 @@ the following general conventions: * Variable segments in the URI begin with a ':' and should replaced with the appropriate resource identifier.
+ +### Collection: Tasks + +**URI:** /plugins/kimchi/tasks + +**Methods:** + +* **GET**: Retrieve a summarized list of current Kimchi specific Tasks (stored +in Kimchi's object store) + +### Resource: Task + +**URI:** /plugins/kimchi/tasks/*:id* + +A task represents an asynchronous operation that is being performed by the +server. + +**Methods:** + +* **GET**: Retrieve the full description of the Task + * id: The Task ID is used to identify this Task in the API. + * status: The current status of the Task + * running: The task is running + * finished: The task has finished successfully + * failed: The task failed + * message: Human-readable details about the Task status + * target_uri: Resource URI related to the Task +* **POST**: *See Task Actions* + +**Actions (POST):** + +*No actions defined* + ### Collection: Virtual Machines
**URI:** /plugins/kimchi/vms diff --git a/src/wok/plugins/kimchi/model/model.py b/src/wok/plugins/kimchi/model/model.py index 0c94f63..39097c4 100644 --- a/src/wok/plugins/kimchi/model/model.py +++ b/src/wok/plugins/kimchi/model/model.py @@ -30,23 +30,37 @@ from libvirtconnection import LibvirtConnection class Model(BaseModel): def __init__(self, libvirt_uri=None, objstore_loc=None):
+ def get_instances(module_name): + instances = [] + module = import_module(module_name) + members = inspect.getmembers(module, inspect.isclass) + for cls_name, instance in members: + if inspect.getmodule(instance) == module and \ + cls_name.endswith('Model'): + instances.append(instance) + + return instances + self.objstore = ObjectStore(objstore_loc) self.conn = LibvirtConnection(libvirt_uri) kargs = {'objstore': self.objstore, 'conn': self.conn} + models = []
+ # Import task model from Wok + instances = get_instances('wok.model.tasks') + for instance in instances: + models.append(instance(**kargs)) + + # Import all Kimchi plugin models this = os.path.basename(__file__) this_mod = os.path.splitext(this)[0]
- models = [] for mod_name in listPathModules(os.path.dirname(__file__)): if mod_name.startswith("_") or mod_name == this_mod: continue
- module = import_module('plugins.kimchi.model.' + mod_name) - members = inspect.getmembers(module, inspect.isclass) - for cls_name, instance in members: - if inspect.getmodule(instance) == module: - if cls_name.endswith('Model'): - models.append(instance(**kargs)) + instances = get_instances('plugins.kimchi.model.' + mod_name)
I suspect it should be 'wok.plugins.kimchi.model'
+ for instance in instances: + models.append(instance(**kargs))
return super(Model, self).__init__(models) diff --git a/src/wok/plugins/kimchi/tests/test_host.py b/src/wok/plugins/kimchi/tests/test_host.py index f3da49b..2940f48 100644 --- a/src/wok/plugins/kimchi/tests/test_host.py +++ b/src/wok/plugins/kimchi/tests/test_host.py @@ -128,12 +128,12 @@ class HostTests(unittest.TestCase): task_params = [u'id', u'message', u'status', u'target_uri'] self.assertEquals(sorted(task_params), sorted(task.keys()))
- resp = self.request('/tasks/' + task[u'id'], None, + resp = self.request('/plugins/kimchi/tasks/' + task[u'id'], None, 'GET') task_info = json.loads(resp.read()) self.assertEquals(task_info['status'], 'running') wait_task(_task_lookup, task_info['id']) - resp = self.request('/tasks/' + task[u'id'], None, + resp = self.request('/plugins/kimchi/tasks/' + task[u'id'], None, 'GET') task_info = json.loads(resp.read()) self.assertEquals(task_info['status'], 'finished') diff --git a/src/wok/plugins/kimchi/tests/test_mock_storagepool.py b/src/wok/plugins/kimchi/tests/test_mock_storagepool.py index 5cf5b3e..ea9843b 100644 --- a/src/wok/plugins/kimchi/tests/test_mock_storagepool.py +++ b/src/wok/plugins/kimchi/tests/test_mock_storagepool.py @@ -61,7 +61,7 @@ class MockStoragepoolTests(unittest.TestCase):
def _task_lookup(self, taskid): return json.loads( - self.request('/tasks/%s' % taskid).read() + self.request('/plugins/kimchi/tasks/%s' % taskid).read() )
def test_storagepool(self): diff --git a/src/wok/plugins/kimchi/tests/test_model_storagevolume.py b/src/wok/plugins/kimchi/tests/test_model_storagevolume.py index 8812e46..087dd7b 100644 --- a/src/wok/plugins/kimchi/tests/test_model_storagevolume.py +++ b/src/wok/plugins/kimchi/tests/test_model_storagevolume.py @@ -64,7 +64,7 @@ def tearDownModule(): def _do_volume_test(self, model, host, ssl_port, pool_name): def _task_lookup(taskid): return json.loads( - self.request('/tasks/%s' % taskid).read() + self.request('/plugins/kimchi/tasks/%s' % taskid).read() )
uri = '/plugins/kimchi/storagepools/%s/storagevolumes' \ @@ -91,7 +91,7 @@ def _do_volume_test(self, model, host, ssl_port, pool_name): task_id = json.loads(resp.read())['id'] wait_task(_task_lookup, task_id) status = json.loads( - self.request('/tasks/%s' % task_id).read() + self.request('/plugins/kimchi/tasks/%s' % task_id).read() ) self.assertEquals('finished', status['status']) vol_info = json.loads(self.request(vol_uri).read()) @@ -137,7 +137,7 @@ def _do_volume_test(self, model, host, ssl_port, pool_name): cloned_vol_name) wait_task(_task_lookup, task['id']) task = json.loads( - self.request('/tasks/%s' % task['id']).read() + self.request('/plugins/kimchi/tasks/%s' % task['id']).read() ) self.assertEquals('finished', task['status']) resp = self.request(uri + '/' + cloned_vol_name.encode('utf-8')) @@ -177,7 +177,7 @@ def _do_volume_test(self, model, host, ssl_port, pool_name): self.assertEquals(202, resp.status) task_id = json.loads(resp.read())['id'] wait_task(_task_lookup, task_id) - status = json.loads(self.request('/tasks/%s' % + status = json.loads(self.request('/plugins/kimchi/tasks/%s' % task_id).read()) self.assertEquals('ready for upload', status['message'])
diff --git a/src/wok/plugins/kimchi/tests/test_rest.py b/src/wok/plugins/kimchi/tests/test_rest.py index 8cf4bd3..e1a2f54 100644 --- a/src/wok/plugins/kimchi/tests/test_rest.py +++ b/src/wok/plugins/kimchi/tests/test_rest.py @@ -335,7 +335,7 @@ class RestTests(unittest.TestCase): task = json.loads(resp.read()) wait_task(self._task_lookup, task['id']) task = json.loads( - self.request('/tasks/%s' % task['id'], '{}').read() + self.request('/plugins/kimchi/tasks/%s' % task['id'], '{}').read() ) self.assertEquals('finished', task['status']) clone_vm_name = task['target_uri'].split('/')[-2] @@ -366,7 +366,7 @@ class RestTests(unittest.TestCase): task = json.loads(resp.read()) wait_task(self._task_lookup, task['id']) task = json.loads( - self.request('/tasks/%s' % task['id']).read() + self.request('/plugins/kimchi/tasks/%s' % task['id']).read() ) self.assertEquals('finished', task['status'])
@@ -404,7 +404,7 @@ class RestTests(unittest.TestCase): task = json.loads(resp.read()) snap_name = task['target_uri'].split('/')[-1] wait_task(self._task_lookup, task['id']) - resp = self.request('/tasks/%s' % task['id'], '{}', + resp = self.request('/plugins/kimchi/tasks/%s' % task['id'], '{}', 'GET') task = json.loads(resp.read()) self.assertEquals('finished', task['status']) @@ -1172,37 +1172,37 @@ class RestTests(unittest.TestCase):
def _task_lookup(self, taskid): return json.loads( - self.request('/tasks/%s' % taskid).read() + self.request('/plugins/kimchi/tasks/%s' % taskid).read() )
def test_tasks(self): - id1 = add_task('/tasks/1', self._async_op, + id1 = add_task('/plugins/kimchi/tasks/1', self._async_op, model.objstore) - id2 = add_task('/tasks/2', self._except_op, + id2 = add_task('/plugins/kimchi/tasks/2', self._except_op, model.objstore) - id3 = add_task('/tasks/3', self._intermid_op, + id3 = add_task('/plugins/kimchi/tasks/3', self._intermid_op, model.objstore)
- target_uri = urllib2.quote('^/tasks/*', safe="") + target_uri = urllib2.quote('^/plugins/kimchi/tasks/*', safe="") filter_data = 'status=running&target_uri=%s' % target_uri tasks = json.loads( - self.request('/tasks?%s' % filter_data).read() + self.request('/plugins/kimchi/tasks?%s' % filter_data).read() ) self.assertEquals(3, len(tasks))
- tasks = json.loads(self.request('/tasks').read()) + tasks = json.loads(self.request('/plugins/kimchi/tasks').read()) tasks_ids = [int(t['id']) for t in tasks] self.assertEquals(set([id1, id2, id3]) - set(tasks_ids), set([])) wait_task(self._task_lookup, id2) foo2 = json.loads( - self.request('/tasks/%s' % id2).read() + self.request('/plugins/kimchi/tasks/%s' % id2).read() ) keys = ['id', 'status', 'message', 'target_uri'] self.assertEquals(sorted(keys), sorted(foo2.keys())) self.assertEquals('failed', foo2['status']) wait_task(self._task_lookup, id3) foo3 = json.loads( - self.request('/tasks/%s' % id3).read() + self.request('/plugins/kimchi/tasks/%s' % id3).read() ) self.assertEquals('in progress', foo3['message']) self.assertEquals('running', foo3['status']) diff --git a/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js b/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js index a16c95e..bb3a53d 100644 --- a/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js +++ b/src/wok/plugins/kimchi/ui/js/src/kimchi.api.js @@ -525,7 +525,7 @@ var kimchi = {
getTask : function(taskId, suc, err) { wok.requestJSON({ - url : 'tasks/' + encodeURIComponent(taskId), + url : 'plugins/kimchi/tasks/' + encodeURIComponent(taskId), type : 'GET', contentType : 'application/json', dataType : 'json', @@ -536,7 +536,7 @@ var kimchi = {
getTasksByFilter : function(filter, suc, err, sync) { wok.requestJSON({ - url : 'tasks?' + filter, + url : 'plugins/kimchi/tasks?' + filter, type : 'GET', contentType : 'application/json', dataType : 'json',
participants (2)
-
Aline Manera
-
Lucio Correia