[PATCHv9 0/8] storage servers and targets supprt

From: Royce Lv <lvroyce@linux.vnet.ibm.com> v8>v9, split two types of filter params, fix nits v7>v8, address model break, change xml construction to libxml v6>v7, adopt lxml to parse xml, move parse params to get() to avoid duplicate code, fix bugs when one server support multiple target type. v5>v6, change GET param support to cover more scenario of filter collected results. v4>v5, remove storage server list reload function, merge storage server and targets v3>v4, fix inconsistency between doc and json schema v1>v3, fix racing problem, fix style. Royce Lv (8): Support params for GET method Add testcase for GET param Storage server: Update API.md storage server: update controller.py storage server: Update model and mockmodel storage target: Update API.md storage target: Update controller and json schema storage target: Add model support docs/API.md | 22 +++++++++++ src/kimchi/API.json | 22 +++++++++++ src/kimchi/control/base.py | 31 +++++++++++---- src/kimchi/control/storagepools.py | 4 +- src/kimchi/control/storageserver.py | 55 ++++++++++++++++++++++++++ src/kimchi/control/storagevolumes.py | 2 +- src/kimchi/mockmodel.py | 30 +++++++++++++++ src/kimchi/model.py | 75 ++++++++++++++++++++++++++++++++++++ src/kimchi/root.py | 2 + tests/test_rest.py | 23 +++++++++++ 10 files changed, 256 insertions(+), 10 deletions(-) create mode 100644 src/kimchi/control/storageserver.py -- 1.8.1.2

From: Royce Lv <lvroyce@linux.vnet.ibm.com> GET filter parameter will be splited in two types: 1. flag_filter: pass to libvirt api and filter when resource query. flag_filters start with "_" 2. field_filter: take effect in filter out final result. field_filters are keys of dict returned by 'GET' Then we can call it like: GET /collection?filter_field=value Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/control/base.py | 31 ++++++++++++++++++++++++------- src/kimchi/control/storagepools.py | 4 ++-- src/kimchi/control/storagevolumes.py | 2 +- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/kimchi/control/base.py b/src/kimchi/control/base.py index 185c8d8..55678b1 100644 --- a/src/kimchi/control/base.py +++ b/src/kimchi/control/base.py @@ -212,10 +212,10 @@ class Collection(object): return res.get() - def _get_resources(self): + def _get_resources(self, flag_filter): try: get_list = getattr(self.model, model_fn(self, 'get_list')) - idents = get_list(*self.model_args) + idents = get_list(*self.model_args, **flag_filter) res_list = [] for ident in idents: # internal text, get_list changes ident to unicode for sorted @@ -234,19 +234,36 @@ class Collection(object): args = self.resource_args + [ident.decode("utf-8")] return self.resource(self.model, *args) - def get(self): - resources = self._get_resources() + def filter_data(self, resources, fields_filter): data = [] for res in resources: - data.append(res.data) + if all(key in res.data and res.data[key] == val \ + for key, val in fields_filter.iteritems()): + data.append(res.data) + return data + + def get(self, filter_params): + def _split_filter(params): + flag_filter = dict() + fields_filter = params + for key, val in params.items(): + if key.startswith('_'): + flag_filter[key] = fields_filter.pop(key) + return flag_filter, fields_filter + + flag_filter, fields_filter = _split_filter(filter_params) + resources = self._get_resources(flag_filter) + data = self.filter_data(resources, fields_filter) return kimchi.template.render(get_class_name(self), data) @cherrypy.expose - def index(self, *args): + def index(self, *args, **kwargs): method = validate_method(('GET', 'POST')) if method == 'GET': try: - return self.get() + filter_params = cherrypy.request.params + validate_params(filter_params, self, 'get_list') + return self.get(filter_params) except InvalidOperation, param: error = "Invalid operation: '%s'" % param raise cherrypy.HTTPError(400, error) diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py index 782f5a6..e3236a7 100644 --- a/src/kimchi/control/storagepools.py +++ b/src/kimchi/control/storagepools.py @@ -63,9 +63,9 @@ class StoragePools(Collection): return resp - def _get_resources(self): + def _get_resources(self, filter_params): try: - res_list = super(StoragePools, self)._get_resources() + res_list = super(StoragePools, self)._get_resources(filter_params) # Append reserved pools isos = getattr(self, ISO_POOL_NAME) isos.lookup() diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py index d541807..cd15bcc 100644 --- a/src/kimchi/control/storagevolumes.py +++ b/src/kimchi/control/storagevolumes.py @@ -70,7 +70,7 @@ class IsoVolumes(Collection): super(IsoVolumes, self).__init__(model) self.pool = pool - def get(self): + def get(self, filter_params): res_list = [] try: get_list = getattr(self.model, model_fn(self, 'get_list')) -- 1.8.1.2

On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
GET filter parameter will be splited in two types: 1. flag_filter: pass to libvirt api and filter when resource query. flag_filters start with "_" 2. field_filter: take effect in filter out final result. field_filters are keys of dict returned by 'GET'
So can't we assume if a key isn't in dict returned by GET it is a flag_filters?
Then we can call it like: GET /collection?filter_field=value
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/control/base.py | 31 ++++++++++++++++++++++++------- src/kimchi/control/storagepools.py | 4 ++-- src/kimchi/control/storagevolumes.py | 2 +- 3 files changed, 27 insertions(+), 10 deletions(-)
diff --git a/src/kimchi/control/base.py b/src/kimchi/control/base.py index 185c8d8..55678b1 100644 --- a/src/kimchi/control/base.py +++ b/src/kimchi/control/base.py @@ -212,10 +212,10 @@ class Collection(object):
return res.get()
- def _get_resources(self): + def _get_resources(self, flag_filter): try: get_list = getattr(self.model, model_fn(self, 'get_list')) - idents = get_list(*self.model_args) + idents = get_list(*self.model_args, **flag_filter) res_list = [] for ident in idents: # internal text, get_list changes ident to unicode for sorted @@ -234,19 +234,36 @@ class Collection(object): args = self.resource_args + [ident.decode("utf-8")] return self.resource(self.model, *args)
- def get(self): - resources = self._get_resources() + def filter_data(self, resources, fields_filter): data = [] for res in resources: - data.append(res.data) + if all(key in res.data and res.data[key] == val \ + for key, val in fields_filter.iteritems()): + data.append(res.data) + return data + + def get(self, filter_params): + def _split_filter(params): + flag_filter = dict() + fields_filter = params + for key, val in params.items(): + if key.startswith('_'): + flag_filter[key] = fields_filter.pop(key) + return flag_filter, fields_filter + + flag_filter, fields_filter = _split_filter(filter_params) + resources = self._get_resources(flag_filter) + data = self.filter_data(resources, fields_filter) return kimchi.template.render(get_class_name(self), data)
@cherrypy.expose - def index(self, *args): + def index(self, *args, **kwargs): method = validate_method(('GET', 'POST')) if method == 'GET': try: - return self.get() + filter_params = cherrypy.request.params + validate_params(filter_params, self, 'get_list') + return self.get(filter_params) except InvalidOperation, param: error = "Invalid operation: '%s'" % param raise cherrypy.HTTPError(400, error) diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py index 782f5a6..e3236a7 100644 --- a/src/kimchi/control/storagepools.py +++ b/src/kimchi/control/storagepools.py @@ -63,9 +63,9 @@ class StoragePools(Collection):
return resp
- def _get_resources(self): + def _get_resources(self, filter_params): try: - res_list = super(StoragePools, self)._get_resources() + res_list = super(StoragePools, self)._get_resources(filter_params) # Append reserved pools isos = getattr(self, ISO_POOL_NAME) isos.lookup() diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py index d541807..cd15bcc 100644 --- a/src/kimchi/control/storagevolumes.py +++ b/src/kimchi/control/storagevolumes.py @@ -70,7 +70,7 @@ class IsoVolumes(Collection): super(IsoVolumes, self).__init__(model) self.pool = pool
- def get(self): + def get(self, filter_params): res_list = [] try: get_list = getattr(self.model, model_fn(self, 'get_list'))

On 2014年01月21日 03:40, Aline Manera wrote:
On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
GET filter parameter will be splited in two types: 1. flag_filter: pass to libvirt api and filter when resource query. flag_filters start with "_" 2. field_filter: take effect in filter out final result. field_filters are keys of dict returned by 'GET'
So can't we assume if a key isn't in dict returned by GET it is a flag_filters? Yes, we can, inside we use "_" to distinguish params, but users don't need to worry about this.
Then we can call it like: GET /collection?filter_field=value
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/control/base.py | 31 ++++++++++++++++++++++++------- src/kimchi/control/storagepools.py | 4 ++-- src/kimchi/control/storagevolumes.py | 2 +- 3 files changed, 27 insertions(+), 10 deletions(-)
diff --git a/src/kimchi/control/base.py b/src/kimchi/control/base.py index 185c8d8..55678b1 100644 --- a/src/kimchi/control/base.py +++ b/src/kimchi/control/base.py @@ -212,10 +212,10 @@ class Collection(object):
return res.get()
- def _get_resources(self): + def _get_resources(self, flag_filter): try: get_list = getattr(self.model, model_fn(self, 'get_list')) - idents = get_list(*self.model_args) + idents = get_list(*self.model_args, **flag_filter) res_list = [] for ident in idents: # internal text, get_list changes ident to unicode for sorted @@ -234,19 +234,36 @@ class Collection(object): args = self.resource_args + [ident.decode("utf-8")] return self.resource(self.model, *args)
- def get(self): - resources = self._get_resources() + def filter_data(self, resources, fields_filter): data = [] for res in resources: - data.append(res.data) + if all(key in res.data and res.data[key] == val \ + for key, val in fields_filter.iteritems()): + data.append(res.data) + return data + + def get(self, filter_params): + def _split_filter(params): + flag_filter = dict() + fields_filter = params + for key, val in params.items(): + if key.startswith('_'): + flag_filter[key] = fields_filter.pop(key) + return flag_filter, fields_filter + + flag_filter, fields_filter = _split_filter(filter_params) + resources = self._get_resources(flag_filter) + data = self.filter_data(resources, fields_filter) return kimchi.template.render(get_class_name(self), data)
@cherrypy.expose - def index(self, *args): + def index(self, *args, **kwargs): method = validate_method(('GET', 'POST')) if method == 'GET': try: - return self.get() + filter_params = cherrypy.request.params + validate_params(filter_params, self, 'get_list') + return self.get(filter_params) except InvalidOperation, param: error = "Invalid operation: '%s'" % param raise cherrypy.HTTPError(400, error) diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py index 782f5a6..e3236a7 100644 --- a/src/kimchi/control/storagepools.py +++ b/src/kimchi/control/storagepools.py @@ -63,9 +63,9 @@ class StoragePools(Collection):
return resp
- def _get_resources(self): + def _get_resources(self, filter_params): try: - res_list = super(StoragePools, self)._get_resources() + res_list = super(StoragePools, self)._get_resources(filter_params) # Append reserved pools isos = getattr(self, ISO_POOL_NAME) isos.lookup() diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py index d541807..cd15bcc 100644 --- a/src/kimchi/control/storagevolumes.py +++ b/src/kimchi/control/storagevolumes.py @@ -70,7 +70,7 @@ class IsoVolumes(Collection): super(IsoVolumes, self).__init__(model) self.pool = pool
- def get(self): + def get(self, filter_params): res_list = [] try: get_list = getattr(self.model, model_fn(self, 'get_list'))

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Add a testcase to test GET param passing and demo how GET param work with current model implementation,which means: 1. change the API.json 2. wrap its model implementation to accept parameters Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- tests/test_rest.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_rest.py b/tests/test_rest.py index a8e5842..dd2747b 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1240,6 +1240,29 @@ class RestTests(unittest.TestCase): self.assertIn('net_recv_rate', stats) self.assertIn('net_sent_rate', stats) + def test_get_param(self): + req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'}) + self.request('/templates', req, 'POST') + + # Create a VM + req = json.dumps({'name': 'test-vm1', 'template': '/templates/test'}) + resp = self.request('/vms', req, 'POST') + self.assertEquals(201, resp.status) + req = json.dumps({'name': 'test-vm2', 'template': '/templates/test'}) + resp = self.request('/vms', req, 'POST') + self.assertEquals(201, resp.status) + + resp = request(host, port, '/vms') + self.assertEquals(200, resp.status) + res = json.loads(resp.read()) + self.assertEquals(2, len(res)) + + resp = request(host, port, '/vms?name=test-vm1') + self.assertEquals(200, resp.status) + res = json.loads(resp.read()) + self.assertEquals(1, len(res)) + self.assertEquals('test-vm1', res[0]['name']) + class HttpsRestTests(RestTests): """ -- 1.8.1.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add a testcase to test GET param passing and demo how GET param work with current model implementation,which means: 1. change the API.json 2. wrap its model implementation to accept parameters
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- tests/test_rest.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/tests/test_rest.py b/tests/test_rest.py index a8e5842..dd2747b 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1240,6 +1240,29 @@ class RestTests(unittest.TestCase): self.assertIn('net_recv_rate', stats) self.assertIn('net_sent_rate', stats)
+ def test_get_param(self): + req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'}) + self.request('/templates', req, 'POST') + + # Create a VM + req = json.dumps({'name': 'test-vm1', 'template': '/templates/test'}) + resp = self.request('/vms', req, 'POST') + self.assertEquals(201, resp.status) + req = json.dumps({'name': 'test-vm2', 'template': '/templates/test'}) + resp = self.request('/vms', req, 'POST') + self.assertEquals(201, resp.status) + + resp = request(host, port, '/vms') + self.assertEquals(200, resp.status) + res = json.loads(resp.read()) + self.assertEquals(2, len(res)) + + resp = request(host, port, '/vms?name=test-vm1') + self.assertEquals(200, resp.status) + res = json.loads(resp.read()) + self.assertEquals(1, len(res)) + self.assertEquals('test-vm1', res[0]['name']) +
class HttpsRestTests(RestTests): """

Reviewed-by: Crístian Viana <vianac@linux.vnet.ibm.com> Am 20-01-2014 07:32, schrieb lvroyce@linux.vnet.ibm.com:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add a testcase to test GET param passing and demo how GET param work with current model implementation,which means: 1. change the API.json 2. wrap its model implementation to accept parameters
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com>

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Update API.md to specify storage server api. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- docs/API.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/API.md b/docs/API.md index f872eab..88a9a41 100644 --- a/docs/API.md +++ b/docs/API.md @@ -495,6 +495,19 @@ creation. not tested yet * **POST**: *See Configuration Actions* +**Actions (POST):** + +*No actions defined* + +### Collection: Storage Servers + +**URI:** /storageservers + +**Methods:** + +* **GET**: Retrieve a summarized list of used storage servers. + * _target_type: Filter server list with given type, currently support 'netfs'. + ### Collection: Distros **URI:** /config/distros -- 1.8.1.2

On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Update API.md to specify storage server api.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- docs/API.md | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/docs/API.md b/docs/API.md index f872eab..88a9a41 100644 --- a/docs/API.md +++ b/docs/API.md @@ -495,6 +495,19 @@ creation. not tested yet * **POST**: *See Configuration Actions*
+**Actions (POST):** + +*No actions defined* + +### Collection: Storage Servers + +**URI:** /storageservers + +**Methods:** + +* **GET**: Retrieve a summarized list of used storage servers. + * _target_type: Filter server list with given type, currently support 'netfs'.
You need to add Storage Server (Resource) to API.md too ### Resource: Storage Server **URI:** /storageservers/*:host* **Methods:** * **GET**: Retrieve the full description of a Storage Server * host: IP or host name of storage server
+ ### Collection: Distros
**URI:** /config/distros

On 2014年01月21日 03:43, Aline Manera wrote:
On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Update API.md to specify storage server api.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- docs/API.md | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/docs/API.md b/docs/API.md index f872eab..88a9a41 100644 --- a/docs/API.md +++ b/docs/API.md @@ -495,6 +495,19 @@ creation. not tested yet * **POST**: *See Configuration Actions*
+**Actions (POST):** + +*No actions defined* + +### Collection: Storage Servers + +**URI:** /storageservers + +**Methods:** + +* **GET**: Retrieve a summarized list of used storage servers. + * _target_type: Filter server list with given type, currently support 'netfs'.
You need to add Storage Server (Resource) to API.md too
### Resource: Storage Server
**URI:** /storageservers/*:host*
**Methods:**
* **GET**: Retrieve the full description of a Storage Server * host: IP or host name of storage server ACK
+ ### Collection: Distros
**URI:** /config/distros

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Add storage server collection and resource to report used storage server. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/API.json | 11 +++++++++++ src/kimchi/control/storageserver.py | 38 +++++++++++++++++++++++++++++++++++++ src/kimchi/root.py | 2 ++ 3 files changed, 51 insertions(+) create mode 100644 src/kimchi/control/storageserver.py diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 46818d4..9b86164 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -237,6 +237,17 @@ }, "additionalProperties": false }, + "storageservers_get_list": { + "type": "object", + "properties": { + "_target_type": { + "description": "List storage servers of given type", + "type": "string", + "pattern": "^netfs$" + } + }, + "additionalProperties": false + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/storageserver.py b/src/kimchi/control/storageserver.py new file mode 100644 index 0000000..0d4cb05 --- /dev/null +++ b/src/kimchi/control/storageserver.py @@ -0,0 +1,38 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Royce Lv <lvroyce@linux.vnet.ibm.com> +# +# 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 + +from kimchi.control.base import Collection, Resource + + +class StorageServers(Collection): + def __init__(self, model): + super(StorageServers, self).__init__(model) + self.resource = StorageServer + + +class StorageServer(Resource): + def __init__(self, model, ident): + super(StorageServer, self).__init__(model, ident) + + @property + def data(self): + return self.info diff --git a/src/kimchi/root.py b/src/kimchi/root.py index 3cc6321..ec531c0 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -36,6 +36,7 @@ from kimchi.control.interfaces import Interfaces from kimchi.control.networks import Networks from kimchi.control.plugins import Plugins from kimchi.control.storagepools import StoragePools +from kimchi.control.storageserver import StorageServers from kimchi.control.tasks import Tasks from kimchi.control.templates import Templates from kimchi.control.utils import parse_request @@ -60,6 +61,7 @@ class Root(Resource): self.vms = VMs(model) self.templates = Templates(model) self.storagepools = StoragePools(model) + self.storageservers = StorageServers(model) self.interfaces = Interfaces(model) self.networks = Networks(model) self.tasks = Tasks(model) -- 1.8.1.2

On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add storage server collection and resource to report used storage server.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/API.json | 11 +++++++++++ src/kimchi/control/storageserver.py | 38 +++++++++++++++++++++++++++++++++++++ src/kimchi/root.py | 2 ++ 3 files changed, 51 insertions(+) create mode 100644 src/kimchi/control/storageserver.py
Usually the control files are named like the URI So it should be storageservers.py
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 46818d4..9b86164 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -237,6 +237,17 @@ }, "additionalProperties": false }, + "storageservers_get_list": { + "type": "object", + "properties": { + "_target_type": { + "description": "List storage servers of given type", + "type": "string", + "pattern": "^netfs$" + } + }, + "additionalProperties": false + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/storageserver.py b/src/kimchi/control/storageserver.py new file mode 100644 index 0000000..0d4cb05 --- /dev/null +++ b/src/kimchi/control/storageserver.py @@ -0,0 +1,38 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Royce Lv <lvroyce@linux.vnet.ibm.com> +# +# 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 + +from kimchi.control.base import Collection, Resource + + +class StorageServers(Collection): + def __init__(self, model): + super(StorageServers, self).__init__(model) + self.resource = StorageServer + + +class StorageServer(Resource): + def __init__(self, model, ident): + super(StorageServer, self).__init__(model, ident) + + @property + def data(self): + return self.info diff --git a/src/kimchi/root.py b/src/kimchi/root.py index 3cc6321..ec531c0 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -36,6 +36,7 @@ from kimchi.control.interfaces import Interfaces from kimchi.control.networks import Networks from kimchi.control.plugins import Plugins from kimchi.control.storagepools import StoragePools +from kimchi.control.storageserver import StorageServers from kimchi.control.tasks import Tasks from kimchi.control.templates import Templates from kimchi.control.utils import parse_request @@ -60,6 +61,7 @@ class Root(Resource): self.vms = VMs(model) self.templates = Templates(model) self.storagepools = StoragePools(model) + self.storageservers = StorageServers(model) self.interfaces = Interfaces(model) self.networks = Networks(model) self.tasks = Tasks(model)

On 2014年01月21日 03:44, Aline Manera wrote:
On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add storage server collection and resource to report used storage server.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/API.json | 11 +++++++++++ src/kimchi/control/storageserver.py | 38 +++++++++++++++++++++++++++++++++++++ src/kimchi/root.py | 2 ++ 3 files changed, 51 insertions(+) create mode 100644 src/kimchi/control/storageserver.py
Usually the control files are named like the URI So it should be storageservers.py Good point, this will be useful when we make them generate automatically.
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 46818d4..9b86164 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -237,6 +237,17 @@ }, "additionalProperties": false }, + "storageservers_get_list": { + "type": "object", + "properties": { + "_target_type": { + "description": "List storage servers of given type", + "type": "string", + "pattern": "^netfs$" + } + }, + "additionalProperties": false + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/storageserver.py b/src/kimchi/control/storageserver.py new file mode 100644 index 0000000..0d4cb05 --- /dev/null +++ b/src/kimchi/control/storageserver.py @@ -0,0 +1,38 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Royce Lv <lvroyce@linux.vnet.ibm.com> +# +# 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 + +from kimchi.control.base import Collection, Resource + + +class StorageServers(Collection): + def __init__(self, model): + super(StorageServers, self).__init__(model) + self.resource = StorageServer + + +class StorageServer(Resource): + def __init__(self, model, ident): + super(StorageServer, self).__init__(model, ident) + + @property + def data(self): + return self.info diff --git a/src/kimchi/root.py b/src/kimchi/root.py index 3cc6321..ec531c0 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -36,6 +36,7 @@ from kimchi.control.interfaces import Interfaces from kimchi.control.networks import Networks from kimchi.control.plugins import Plugins from kimchi.control.storagepools import StoragePools +from kimchi.control.storageserver import StorageServers from kimchi.control.tasks import Tasks from kimchi.control.templates import Templates from kimchi.control.utils import parse_request @@ -60,6 +61,7 @@ class Root(Resource): self.vms = VMs(model) self.templates = Templates(model) self.storagepools = StoragePools(model) + self.storageservers = StorageServers(model) self.interfaces = Interfaces(model) self.networks = Networks(model) self.tasks = Tasks(model)

Reviewed-by: Crístian Viana <vianac@linux.vnet.ibm.com> Am 20-01-2014 07:32, schrieb lvroyce@linux.vnet.ibm.com:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add storage server collection and resource to report used storage server.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com>

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Query all storage pool to retrieve storage server we used. If no query param is given, all supported type will be listed. With param given, only specified type of server is listed. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 30 ++++++++++++++++++++++++++++++ src/kimchi/model.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 4ef3fa6..87e7220 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -408,6 +408,36 @@ class MockModel(object): iso_volumes.append(res) return iso_volumes + def storageservers_get_list(self, _target_type=None): + # FIXME: When added new storage server support, this needs to be updated + target_type = kimchi.model.STORAGE_SOURCES.keys() \ + if not _target_type else [_target_type] + pools = self.storagepools_get_list() + server_list = [] + for pool in pools: + try: + pool_info = self.storagepool_lookup(pool) + if (pool_info['type'] in target_type and + pool_info['source']['addr'] not in server_list): + server_list.append(pool_info['source']['addr']) + except NotFoundError: + pass + + return server_list + + def storageserver_lookup(self, server): + pools = self.storagepools_get_list() + for pool in pools: + try: + pool_info = self.storagepool_lookup(pool) + if pool_info['source'] and pool_info['source']['addr'] == server: + return dict(addr=server) + except NotFoundError: + # Avoid inconsistent pool result because of lease between list and lookup + pass + + raise NotFoundError("storage server %s not used by kimchi" % server) + def dummy_interfaces(self): interfaces = {} ifaces = {"eth1": "nic", "bond0": "bonding", diff --git a/src/kimchi/model.py b/src/kimchi/model.py index 2c6d3a1..6b009c3 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -1300,6 +1300,36 @@ class Model(object): else: raise + def storageservers_get_list(self, _target_type=None): + target_type = STORAGE_SOURCES.keys() if not _target_type else [_target_type] + pools = self.storagepools_get_list() + server_list = [] + for pool in pools: + try: + pool_info = self.storagepool_lookup(pool) + if (pool_info['type'] in target_type and + pool_info['source']['addr'] not in server_list): + # Avoid to add same server for multiple times + # if it hosts more than one storage type + server_list.append(pool_info['source']['addr']) + except NotFoundError: + pass + + return server_list + + def storageserver_lookup(self, server): + pools = self.storagepools_get_list() + for pool in pools: + try: + pool_info = self.storagepool_lookup(pool) + if pool_info['source'] and pool_info['source']['addr'] == server: + return dict(addr=server) + except NotFoundError: + # Avoid inconsistent pool result because of lease between list and lookup + pass + + raise NotFoundError('server %s does not used by kimchi' % server) + def _get_screenshot(self, vm_uuid): with self.objstore as session: try: -- 1.8.1.2

Reviewed-by: Crístian Viana <vianac@linux.vnet.ibm.com> Am 20-01-2014 07:32, schrieb lvroyce@linux.vnet.ibm.com:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Query all storage pool to retrieve storage server we used. If no query param is given, all supported type will be listed. With param given, only specified type of server is listed.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com>

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Add colleciton of storage targets to API.md. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- docs/API.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/API.md b/docs/API.md index 88a9a41..7fbce77 100644 --- a/docs/API.md +++ b/docs/API.md @@ -508,6 +508,15 @@ creation. * **GET**: Retrieve a summarized list of used storage servers. * _target_type: Filter server list with given type, currently support 'netfs'. +### Collection: Storage Targets + +**URI:** /storageservers/*:name*/storagetargets + +**Methods:** + +* **GET**: Retrieve a list of available storage targets. + * _target_type: Filter target list with given type, currently support 'netfs'. + ### Collection: Distros **URI:** /config/distros -- 1.8.1.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add colleciton of storage targets to API.md.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- docs/API.md | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/docs/API.md b/docs/API.md index 88a9a41..7fbce77 100644 --- a/docs/API.md +++ b/docs/API.md @@ -508,6 +508,15 @@ creation. * **GET**: Retrieve a summarized list of used storage servers. * _target_type: Filter server list with given type, currently support 'netfs'.
+### Collection: Storage Targets + +**URI:** /storageservers/*:name*/storagetargets + +**Methods:** + +* **GET**: Retrieve a list of available storage targets. + * _target_type: Filter target list with given type, currently support 'netfs'. + ### Collection: Distros
**URI:** /config/distros

On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add colleciton of storage targets to API.md.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- docs/API.md | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/docs/API.md b/docs/API.md index 88a9a41..7fbce77 100644 --- a/docs/API.md +++ b/docs/API.md @@ -508,6 +508,15 @@ creation. * **GET**: Retrieve a summarized list of used storage servers. * _target_type: Filter server list with given type, currently support 'netfs'.
+### Collection: Storage Targets + +**URI:** /storageservers/*:name*/storagetargets + +**Methods:** + +* **GET**: Retrieve a list of available storage targets. + * _target_type: Filter target list with given type, currently support 'netfs'. +
In addition to the query parameters, you should also list the parameter returned by GET
### Collection: Distros
**URI:** /config/distros

On 2014年01月21日 08:24, Aline Manera wrote:
On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add colleciton of storage targets to API.md.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- docs/API.md | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/docs/API.md b/docs/API.md index 88a9a41..7fbce77 100644 --- a/docs/API.md +++ b/docs/API.md @@ -508,6 +508,15 @@ creation. * **GET**: Retrieve a summarized list of used storage servers. * _target_type: Filter server list with given type, currently support 'netfs'.
+### Collection: Storage Targets + +**URI:** /storageservers/*:name*/storagetargets + +**Methods:** + +* **GET**: Retrieve a list of available storage targets. + * _target_type: Filter target list with given type, currently support 'netfs'. +
In addition to the query parameters, you should also list the parameter returned by GET ACK
### Collection: Distros
**URI:** /config/distros

Reviewed-by: Crístian Viana <vianac@linux.vnet.ibm.com> Am 20-01-2014 07:32, schrieb lvroyce@linux.vnet.ibm.com:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add colleciton of storage targets to API.md.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com>

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Add json schema to validate mandatory param of target_type, also update controller.py. Reload the get_list function because we don't need to query each target. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/API.json | 11 +++++++++++ src/kimchi/control/storageserver.py | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 9b86164..e942824 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -248,6 +248,17 @@ }, "additionalProperties": false }, + "storagetargets_get_list": { + "type": "object", + "properties": { + "_target_type": { + "description": "List storage servers of given type", + "type": "string", + "pattern": "^netfs$" + } + }, + "additionalProperties": false + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/storageserver.py b/src/kimchi/control/storageserver.py index 0d4cb05..7f89bcc 100644 --- a/src/kimchi/control/storageserver.py +++ b/src/kimchi/control/storageserver.py @@ -21,6 +21,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn +import kimchi.template class StorageServers(Collection): @@ -32,7 +34,22 @@ class StorageServers(Collection): class StorageServer(Resource): def __init__(self, model, ident): super(StorageServer, self).__init__(model, ident) + self.storagetargets = StorageTargets(self.model, self.ident.decode("utf-8")) @property def data(self): return self.info + + +class StorageTargets(Collection): + def __init__(self, model, server): + super(StorageTargets, self).__init__(model) + self.server = server + self.resource_args = [self.server, ] + self.model_args = [self.server, ] + + def get(self, filter_params): + res_list = [] + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args, **filter_params) + return kimchi.template.render(get_class_name(self), res_list) -- 1.8.1.2

On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add json schema to validate mandatory param of target_type, also update controller.py. Reload the get_list function because we don't need to query each target.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/API.json | 11 +++++++++++ src/kimchi/control/storageserver.py | 17 +++++++++++++++++ 2 files changed, 28 insertions(+)
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 9b86164..e942824 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -248,6 +248,17 @@ }, "additionalProperties": false }, + "storagetargets_get_list": { + "type": "object", + "properties": { + "_target_type": { + "description": "List storage servers of given type", + "type": "string", + "pattern": "^netfs$" + } + }, + "additionalProperties": false + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/storageserver.py b/src/kimchi/control/storageserver.py index 0d4cb05..7f89bcc 100644 --- a/src/kimchi/control/storageserver.py +++ b/src/kimchi/control/storageserver.py @@ -21,6 +21,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn +import kimchi.template
"import ..." block is before "from ... import ..." block
class StorageServers(Collection): @@ -32,7 +34,22 @@ class StorageServers(Collection): class StorageServer(Resource): def __init__(self, model, ident): super(StorageServer, self).__init__(model, ident) + self.storagetargets = StorageTargets(self.model, self.ident.decode("utf-8"))
@property def data(self): return self.info + + +class StorageTargets(Collection): + def __init__(self, model, server): + super(StorageTargets, self).__init__(model) + self.server = server + self.resource_args = [self.server, ] + self.model_args = [self.server, ] +
+ def get(self, filter_params): + res_list = [] + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args, **filter_params) + return kimchi.template.render(get_class_name(self), res_list)
All get() logic has already being done in Collection class. You don't need to re-implement it again here.

On 2014年01月21日 03:51, Aline Manera wrote:
On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add json schema to validate mandatory param of target_type, also update controller.py. Reload the get_list function because we don't need to query each target.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/API.json | 11 +++++++++++ src/kimchi/control/storageserver.py | 17 +++++++++++++++++ 2 files changed, 28 insertions(+)
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 9b86164..e942824 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -248,6 +248,17 @@ }, "additionalProperties": false }, + "storagetargets_get_list": { + "type": "object", + "properties": { + "_target_type": { + "description": "List storage servers of given type", + "type": "string", + "pattern": "^netfs$" + } + }, + "additionalProperties": false + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/storageserver.py b/src/kimchi/control/storageserver.py index 0d4cb05..7f89bcc 100644 --- a/src/kimchi/control/storageserver.py +++ b/src/kimchi/control/storageserver.py @@ -21,6 +21,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn +import kimchi.template
"import ..." block is before "from ... import ..." block ACK
class StorageServers(Collection): @@ -32,7 +34,22 @@ class StorageServers(Collection): class StorageServer(Resource): def __init__(self, model, ident): super(StorageServer, self).__init__(model, ident) + self.storagetargets = StorageTargets(self.model, self.ident.decode("utf-8"))
@property def data(self): return self.info + + +class StorageTargets(Collection): + def __init__(self, model, server): + super(StorageTargets, self).__init__(model) + self.server = server + self.resource_args = [self.server, ] + self.model_args = [self.server, ] +
+ def get(self, filter_params): + res_list = [] + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args, **filter_params) + return kimchi.template.render(get_class_name(self), res_list)
All get() logic has already being done in Collection class. You don't need to re-implement it again here.
Because StorageTargets does not have resource and cannot iterate resource lookup().

On 01/21/2014 07:40 AM, Royce Lv wrote:
On 2014年01月21日 03:51, Aline Manera wrote:
On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add json schema to validate mandatory param of target_type, also update controller.py. Reload the get_list function because we don't need to query each target.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/API.json | 11 +++++++++++ src/kimchi/control/storageserver.py | 17 +++++++++++++++++ 2 files changed, 28 insertions(+)
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 9b86164..e942824 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -248,6 +248,17 @@ }, "additionalProperties": false }, + "storagetargets_get_list": { + "type": "object", + "properties": { + "_target_type": { + "description": "List storage servers of given type", + "type": "string", + "pattern": "^netfs$" + } + }, + "additionalProperties": false + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/storageserver.py b/src/kimchi/control/storageserver.py index 0d4cb05..7f89bcc 100644 --- a/src/kimchi/control/storageserver.py +++ b/src/kimchi/control/storageserver.py @@ -21,6 +21,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from kimchi.control.base import Collection, Resource +from kimchi.control.utils import get_class_name, model_fn +import kimchi.template
"import ..." block is before "from ... import ..." block ACK
class StorageServers(Collection): @@ -32,7 +34,22 @@ class StorageServers(Collection): class StorageServer(Resource): def __init__(self, model, ident): super(StorageServer, self).__init__(model, ident) + self.storagetargets = StorageTargets(self.model, self.ident.decode("utf-8"))
@property def data(self): return self.info + + +class StorageTargets(Collection): + def __init__(self, model, server): + super(StorageTargets, self).__init__(model) + self.server = server + self.resource_args = [self.server, ] + self.model_args = [self.server, ] +
+ def get(self, filter_params): + res_list = [] + get_list = getattr(self.model, model_fn(self, 'get_list')) + res_list = get_list(*self.model_args, **filter_params) + return kimchi.template.render(get_class_name(self), res_list)
All get() logic has already being done in Collection class. You don't need to re-implement it again here.
Because StorageTargets does not have resource and cannot iterate resource lookup().
Ok. Thanks

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Construct xml to query storage targets information from storage server. Use lxml to parse result instead of etree. Use lxml to parse target query result. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/kimchi/model.py b/src/kimchi/model.py index 6b009c3..65f1b83 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -30,6 +30,7 @@ import ipaddr import json import libvirt import logging +import lxml.etree as ET import os import platform import psutil @@ -45,6 +46,8 @@ import uuid from cherrypy.process.plugins import BackgroundTask from cherrypy.process.plugins import SimplePlugin from collections import defaultdict +from lxml import objectify +from lxml.builder import E from xml.etree import ElementTree @@ -1330,6 +1333,24 @@ class Model(object): raise NotFoundError('server %s does not used by kimchi' % server) + def storagetargets_get_list(self, storage_server, _target_type=None): + target_types = STORAGE_SOURCES.keys() if not _target_type else [_target_type] + target_list = list() + + for target_type in target_types: + xml = _get_storage_server_spec(server=storage_server, target_type=target_type) + conn = self.conn.get() + + try: + ret = conn.findStoragePoolSources(target_type, xml, 0) + except libvirt.libvirtError as e: + kimchi_log.warning("Query storage pool source fails because of %s", + e.get_error_message()) + continue + + target_list.extend(_parse_target_source_result(target_type, ret)) + return target_list + def _get_screenshot(self, vm_uuid): with self.objstore as session: try: @@ -1540,6 +1561,30 @@ class LibvirtVMScreenshot(VMScreenshot): os.close(fd) +def _parse_target_source_result(target_type, xml_str): + root = objectify.fromstring(xml_str) + ret = [] + for source in root.getchildren(): + if target_type == 'netfs': + host_name = source.host.get('name') + target_path = source.dir.get('path') + type = source.format.get('type') + ret.append(dict(host=host_name, target_type=type, target=target_path)) + return ret + + +def _get_storage_server_spec(**kwargs): + # Required parameters: + # server: + # target_type: + extra_args = [] + if kwargs['target_type'] == 'netfs': + extra_args.append(E.format(type='nfs')) + obj = E.source(E.host(name=kwargs['server']), *extra_args) + xml = ET.tostring(obj) + return xml + + class StoragePoolDef(object): @classmethod def create(cls, poolArgs): -- 1.8.1.2

The patch looks good. But we need to care about Ubuntu issue. There are 2 possible solutions for that: 1) instead of using libvirt to discover storage targets we always use the commands directly: showmount and iscsiadm 2) create feature tests to identify the libvirt problem and if so use the commands directly only in case of failure Checking the libvirt code it uses showmount and iscsiadm internally: /usr/sbin/showmount --no-headers --exports localhost /usr/sbin/iscsiadm --mode discovery --type sendtargets --portal localhost:3260,1 So I tend to prefer the solution 1. Anyone else would like to join the discussion? On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Construct xml to query storage targets information from storage server. Use lxml to parse result instead of etree. Use lxml to parse target query result.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
diff --git a/src/kimchi/model.py b/src/kimchi/model.py index 6b009c3..65f1b83 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -30,6 +30,7 @@ import ipaddr import json import libvirt import logging +import lxml.etree as ET import os import platform import psutil @@ -45,6 +46,8 @@ import uuid from cherrypy.process.plugins import BackgroundTask from cherrypy.process.plugins import SimplePlugin from collections import defaultdict +from lxml import objectify +from lxml.builder import E from xml.etree import ElementTree
@@ -1330,6 +1333,24 @@ class Model(object):
raise NotFoundError('server %s does not used by kimchi' % server)
+ def storagetargets_get_list(self, storage_server, _target_type=None): + target_types = STORAGE_SOURCES.keys() if not _target_type else [_target_type] + target_list = list() + + for target_type in target_types: + xml = _get_storage_server_spec(server=storage_server, target_type=target_type) + conn = self.conn.get() + + try: + ret = conn.findStoragePoolSources(target_type, xml, 0) + except libvirt.libvirtError as e: + kimchi_log.warning("Query storage pool source fails because of %s", + e.get_error_message()) + continue + + target_list.extend(_parse_target_source_result(target_type, ret)) + return target_list + def _get_screenshot(self, vm_uuid): with self.objstore as session: try: @@ -1540,6 +1561,30 @@ class LibvirtVMScreenshot(VMScreenshot): os.close(fd)
+def _parse_target_source_result(target_type, xml_str): + root = objectify.fromstring(xml_str) + ret = [] + for source in root.getchildren(): + if target_type == 'netfs': + host_name = source.host.get('name') + target_path = source.dir.get('path') + type = source.format.get('type') + ret.append(dict(host=host_name, target_type=type, target=target_path)) + return ret + + +def _get_storage_server_spec(**kwargs): + # Required parameters: + # server: + # target_type: + extra_args = [] + if kwargs['target_type'] == 'netfs': + extra_args.append(E.format(type='nfs')) + obj = E.source(E.host(name=kwargs['server']), *extra_args) + xml = ET.tostring(obj) + return xml + + class StoragePoolDef(object): @classmethod def create(cls, poolArgs):

On 01/21/2014 04:25 AM, Aline Manera wrote:
The patch looks good.
But we need to care about Ubuntu issue.
There are 2 possible solutions for that:
1) instead of using libvirt to discover storage targets we always use the commands directly: showmount and iscsiadm
Are there anyone can explain the davantage and disavantage between libvirt and use commands directly?
2) create feature tests to identify the libvirt problem and if so use the commands directly only in case of failure
Checking the libvirt code it uses showmount and iscsiadm internally:
/usr/sbin/showmount --no-headers --exports localhost /usr/sbin/iscsiadm --mode discovery --type sendtargets --portal localhost:3260,1
So I tend to prefer the solution 1.
Anyone else would like to join the discussion?
On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Construct xml to query storage targets information from storage server. Use lxml to parse result instead of etree. Use lxml to parse target query result.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
diff --git a/src/kimchi/model.py b/src/kimchi/model.py index 6b009c3..65f1b83 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -30,6 +30,7 @@ import ipaddr import json import libvirt import logging +import lxml.etree as ET import os import platform import psutil @@ -45,6 +46,8 @@ import uuid from cherrypy.process.plugins import BackgroundTask from cherrypy.process.plugins import SimplePlugin from collections import defaultdict +from lxml import objectify +from lxml.builder import E from xml.etree import ElementTree
@@ -1330,6 +1333,24 @@ class Model(object):
raise NotFoundError('server %s does not used by kimchi' % server)
+ def storagetargets_get_list(self, storage_server, _target_type=None): + target_types = STORAGE_SOURCES.keys() if not _target_type else [_target_type] + target_list = list() + + for target_type in target_types: + xml = _get_storage_server_spec(server=storage_server, target_type=target_type) + conn = self.conn.get() + + try: + ret = conn.findStoragePoolSources(target_type, xml, 0) + except libvirt.libvirtError as e: + kimchi_log.warning("Query storage pool source fails because of %s", + e.get_error_message()) + continue + + target_list.extend(_parse_target_source_result(target_type, ret)) + return target_list + def _get_screenshot(self, vm_uuid): with self.objstore as session: try: @@ -1540,6 +1561,30 @@ class LibvirtVMScreenshot(VMScreenshot): os.close(fd)
+def _parse_target_source_result(target_type, xml_str): + root = objectify.fromstring(xml_str) + ret = [] + for source in root.getchildren(): + if target_type == 'netfs': + host_name = source.host.get('name') + target_path = source.dir.get('path') + type = source.format.get('type') + ret.append(dict(host=host_name, target_type=type, target=target_path)) + return ret + + +def _get_storage_server_spec(**kwargs): + # Required parameters: + # server: + # target_type: + extra_args = [] + if kwargs['target_type'] == 'netfs': + extra_args.append(E.format(type='nfs')) + obj = E.source(E.host(name=kwargs['server']), *extra_args) + xml = ET.tostring(obj) + return xml + + class StoragePoolDef(object): @classmethod def create(cls, poolArgs):
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 01/21/2014 12:02 AM, zhoumeina wrote:
On 01/21/2014 04:25 AM, Aline Manera wrote:
The patch looks good.
But we need to care about Ubuntu issue.
There are 2 possible solutions for that:
1) instead of using libvirt to discover storage targets we always use the commands directly: showmount and iscsiadm
Are there anyone can explain the davantage and disavantage between libvirt and use commands directly?
Hi Meina, From my view, the advantage for using libvirt is we don't need to verify the "target_type" value in order to take an action. Libvirt only expects a xml and it returns the values for us. For other hand, we can eliminate the libvirt level and run the commands direclty.
2) create feature tests to identify the libvirt problem and if so use the commands directly only in case of failure
Checking the libvirt code it uses showmount and iscsiadm internally:
/usr/sbin/showmount --no-headers --exports localhost /usr/sbin/iscsiadm --mode discovery --type sendtargets --portal localhost:3260,1
So I tend to prefer the solution 1.
Anyone else would like to join the discussion?
On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Construct xml to query storage targets information from storage server. Use lxml to parse result instead of etree. Use lxml to parse target query result.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
diff --git a/src/kimchi/model.py b/src/kimchi/model.py index 6b009c3..65f1b83 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -30,6 +30,7 @@ import ipaddr import json import libvirt import logging +import lxml.etree as ET import os import platform import psutil @@ -45,6 +46,8 @@ import uuid from cherrypy.process.plugins import BackgroundTask from cherrypy.process.plugins import SimplePlugin from collections import defaultdict +from lxml import objectify +from lxml.builder import E from xml.etree import ElementTree
@@ -1330,6 +1333,24 @@ class Model(object):
raise NotFoundError('server %s does not used by kimchi' % server)
+ def storagetargets_get_list(self, storage_server, _target_type=None): + target_types = STORAGE_SOURCES.keys() if not _target_type else [_target_type] + target_list = list() + + for target_type in target_types: + xml = _get_storage_server_spec(server=storage_server, target_type=target_type) + conn = self.conn.get() + + try: + ret = conn.findStoragePoolSources(target_type, xml, 0) + except libvirt.libvirtError as e: + kimchi_log.warning("Query storage pool source fails because of %s", + e.get_error_message()) + continue + + target_list.extend(_parse_target_source_result(target_type, ret)) + return target_list + def _get_screenshot(self, vm_uuid): with self.objstore as session: try: @@ -1540,6 +1561,30 @@ class LibvirtVMScreenshot(VMScreenshot): os.close(fd)
+def _parse_target_source_result(target_type, xml_str): + root = objectify.fromstring(xml_str) + ret = [] + for source in root.getchildren(): + if target_type == 'netfs': + host_name = source.host.get('name') + target_path = source.dir.get('path') + type = source.format.get('type') + ret.append(dict(host=host_name, target_type=type, target=target_path)) + return ret + + +def _get_storage_server_spec(**kwargs): + # Required parameters: + # server: + # target_type: + extra_args = [] + if kwargs['target_type'] == 'netfs': + extra_args.append(E.format(type='nfs')) + obj = E.source(E.host(name=kwargs['server']), *extra_args) + xml = ET.tostring(obj) + return xml + + class StoragePoolDef(object): @classmethod def create(cls, poolArgs):
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 2014年01月21日 10:02, zhoumeina wrote:
On 01/21/2014 04:25 AM, Aline Manera wrote:
The patch looks good.
But we need to care about Ubuntu issue.
There are 2 possible solutions for that:
1) instead of using libvirt to discover storage targets we always use the commands directly: showmount and iscsiadm
Are there anyone can explain the davantage and disavantage between libvirt and use commands directly?
2) create feature tests to identify the libvirt problem and if so use the commands directly only in case of failure
Checking the libvirt code it uses showmount and iscsiadm internally:
/usr/sbin/showmount --no-headers --exports localhost /usr/sbin/iscsiadm --mode discovery --type sendtargets --portal localhost:3260,1
So I tend to prefer the solution 1.
Libvirt interface already implement iscsi and showmount in a uniformed api, is easy to use and we just need to parse the result, cons are it has bug in Ubuntu 13.04 so need to add featrue test. Write on our own is easy to control and can avoid bug of 13.04.
Anyone else would like to join the discussion?
On 01/20/2014 07:32 AM, lvroyce@linux.vnet.ibm.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Construct xml to query storage targets information from storage server. Use lxml to parse result instead of etree. Use lxml to parse target query result.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
diff --git a/src/kimchi/model.py b/src/kimchi/model.py index 6b009c3..65f1b83 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -30,6 +30,7 @@ import ipaddr import json import libvirt import logging +import lxml.etree as ET import os import platform import psutil @@ -45,6 +46,8 @@ import uuid from cherrypy.process.plugins import BackgroundTask from cherrypy.process.plugins import SimplePlugin from collections import defaultdict +from lxml import objectify +from lxml.builder import E from xml.etree import ElementTree
@@ -1330,6 +1333,24 @@ class Model(object):
raise NotFoundError('server %s does not used by kimchi' % server)
+ def storagetargets_get_list(self, storage_server, _target_type=None): + target_types = STORAGE_SOURCES.keys() if not _target_type else [_target_type] + target_list = list() + + for target_type in target_types: + xml = _get_storage_server_spec(server=storage_server, target_type=target_type) + conn = self.conn.get() + + try: + ret = conn.findStoragePoolSources(target_type, xml, 0) + except libvirt.libvirtError as e: + kimchi_log.warning("Query storage pool source fails because of %s", + e.get_error_message()) + continue + + target_list.extend(_parse_target_source_result(target_type, ret)) + return target_list + def _get_screenshot(self, vm_uuid): with self.objstore as session: try: @@ -1540,6 +1561,30 @@ class LibvirtVMScreenshot(VMScreenshot): os.close(fd)
+def _parse_target_source_result(target_type, xml_str): + root = objectify.fromstring(xml_str) + ret = [] + for source in root.getchildren(): + if target_type == 'netfs': + host_name = source.host.get('name') + target_path = source.dir.get('path') + type = source.format.get('type') + ret.append(dict(host=host_name, target_type=type, target=target_path)) + return ret + + +def _get_storage_server_spec(**kwargs): + # Required parameters: + # server: + # target_type: + extra_args = [] + if kwargs['target_type'] == 'netfs': + extra_args.append(E.format(type='nfs')) + obj = E.source(E.host(name=kwargs['server']), *extra_args) + xml = ET.tostring(obj) + return xml + + class StoragePoolDef(object): @classmethod def create(cls, poolArgs):
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
participants (5)
-
Aline Manera
-
Crístian Viana
-
lvroyce@linux.vnet.ibm.com
-
Royce Lv
-
zhoumeina