From 54528b038157b07a63bbfd9bb1901bcf82a0fa43 Mon Sep 17 00:00:00 2001 From: Dongfeng Huang Date: Thu, 6 Apr 2017 16:01:13 +0800 Subject: [PATCH] Fix the issue in tempest test for volumes 1. What is the problem Glance V1 API has been deprecated, so the V2 API should be used instead. And pagination tempest test cases were added, but Trio2o doesn't support volumes pagination yet. 2. What is the solution to the problem Use V2 API in Glance client, and add pagination support for cinder volumes. 3. What the features need to be implemented to the Trio2o to realize the solution N/A Change-Id: Ia580cc5f848be82bfc1b880f3fcdf8d7d2615789 --- test-requirements.txt | 2 +- trio2o/cinder_apigw/controllers/volume.py | 13 ++++-- trio2o/common/client.py | 4 +- trio2o/common/resource_handle.py | 4 +- trio2o/nova_apigw/controllers/image.py | 53 ++++++++++++++++++++--- trio2o/tests/unit/common/test_client.py | 13 ------ 6 files changed, 60 insertions(+), 29 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 9ab0467..11fa4e8 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking<0.11,>=0.10.2 +hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 coverage>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD diff --git a/trio2o/cinder_apigw/controllers/volume.py b/trio2o/cinder_apigw/controllers/volume.py index 670005a..5451c88 100644 --- a/trio2o/cinder_apigw/controllers/volume.py +++ b/trio2o/cinder_apigw/controllers/volume.py @@ -157,7 +157,7 @@ class VolumeController(rest.RestController): context = t_context.extract_context_from_environ() if _id == 'detail': - return {'volumes': self._get_all(context)} + return self._get_all(context) # TODO(joehuang): get the release of top and bottom t_release = cons.R_MITAKA @@ -218,12 +218,14 @@ class VolumeController(rest.RestController): # now combined with 'detail' context = t_context.extract_context_from_environ() - return {'volumes': self._get_all(context)} + return self._get_all(context) def _get_all(self, context): # TODO(joehuang): query optimization for pagination, sort, etc - ret = [] + ret = {} + ret['volumes'] = [] + pods = az_ag.list_pods_by_tenant(context, self.tenant_id) for pod in pods: if pod['pod_name'] == '': @@ -278,7 +280,10 @@ class VolumeController(rest.RestController): vol['availability_zone'] = pod['az_name'] - ret.extend(b_ret_body['volumes']) + ret['volumes'].extend(b_ret_body['volumes']) + + if b_ret_body.get('volumes_links'): + ret['volumes_links'] = b_ret_body['volumes_links'] return ret @expose(generic=True, template='json') diff --git a/trio2o/common/client.py b/trio2o/common/client.py index 648b1b4..eafb58e 100644 --- a/trio2o/common/client.py +++ b/trio2o/common/client.py @@ -229,8 +229,6 @@ class Client(object): def _get_config_with_retry(self, cxt, filters, pod, service, retry): conf_list = api.list_pod_service_configurations(cxt, filters) - if len(conf_list) > 1: - raise exceptions.EndpointNotUnique(pod, service) if len(conf_list) == 0: if not retry: raise exceptions.EndpointNotFound(pod, service) @@ -295,7 +293,7 @@ class Client(object): cxt, config_filters) if len(config_list) > 1: - raise exceptions.EndpointNotUnique(pod_id, service) + continue if len(config_list) == 1: config_id = config_list[0]['service_id'] update_dict = { diff --git a/trio2o/common/resource_handle.py b/trio2o/common/resource_handle.py index 299b781..478c15d 100644 --- a/trio2o/common/resource_handle.py +++ b/trio2o/common/resource_handle.py @@ -84,7 +84,7 @@ class GlanceResourceHandle(ResourceHandle): support_resource = {'image': LIST | GET} def _get_client(self, cxt): - return g_client.Client('1', + return g_client.Client('2', token=cxt.auth_token, auth_url=self.auth_url, endpoint=self.endpoint_url, @@ -105,7 +105,7 @@ class GlanceResourceHandle(ResourceHandle): try: client = self._get_client(cxt) collection = '%ss' % resource - return getattr(client, collection).get(resource_id).to_dict() + return getattr(client, collection).get(resource_id) except g_exceptions.InvalidEndpoint: self.endpoint_url = None raise exceptions.EndpointNotAvailable('glance', diff --git a/trio2o/nova_apigw/controllers/image.py b/trio2o/nova_apigw/controllers/image.py index bc8e6e3..1cea02d 100644 --- a/trio2o/nova_apigw/controllers/image.py +++ b/trio2o/nova_apigw/controllers/image.py @@ -23,6 +23,16 @@ from trio2o.common.i18n import _ from trio2o.common import utils import trio2o.db.api as db_api +SUPPORTED_FILTERS = { + 'name': 'name', + 'status': 'status', + 'changes-since': 'changes-since', + 'server': 'property-instance_uuid', + 'type': 'property-image_type', + 'minRam': 'min_ram', + 'minDisk': 'min_disk', +} + class ImageController(rest.RestController): @@ -90,27 +100,58 @@ class ImageController(rest.RestController): 'minRam': int(image.get('min_ram') or 0), 'minDisk': int(image.get('min_disk') or 0), 'metadata': image.get('properties', {}), - 'created': self._format_date(image.get('created_at')), - 'updated': self._format_date(image.get('updated_at')), + 'created': image.get('created_at'), + 'updated': image.get('updated_at'), 'status': self._get_status(image), 'progress': self._get_progress(image), 'links': self._get_links(context, image) } @expose(generic=True, template='json') - def get_one(self, _id): + def get_one(self, _id, **kwargs): context = t_context.extract_context_from_environ() if _id == 'detail': - return self.get_all() + return self.get_all(**kwargs) image = self.client.get_images(context, _id) if not image: return utils.format_nova_error(404, _('Image not found')) return {'image': self._construct_show_image_entry(context, image)} @expose(generic=True, template='json') - def get_all(self): + def get_all(self, **kwargs): context = t_context.extract_context_from_environ() - images = self.client.list_images(context) + filters = self._get_filters(kwargs) + filters = [{'key': key, + 'comparator': 'eq', + 'value': value} for key, value in filters.iteritems()] + images = self.client.list_images(context, filters=filters) ret_images = [self._construct_list_image_entry( context, image) for image in images] return {'images': ret_images} + + def _get_filters(self, params): + """Return a dictionary of query param filters from the request. + + :param params: the URI params coming from the wsgi layer + :return a dict of key/value filters + """ + filters = {} + for param in params: + if param in SUPPORTED_FILTERS or param.startswith('property-'): + # map filter name or carry through if property-* + filter_name = SUPPORTED_FILTERS.get(param, param) + filters[filter_name] = params.get(param) + + # ensure server filter is the instance uuid + filter_name = 'property-instance_uuid' + try: + filters[filter_name] = filters[filter_name].rsplit('/', 1)[1] + except (AttributeError, IndexError, KeyError): + pass + + filter_name = 'status' + if filter_name in filters: + # The Image API expects us to use lowercase strings for status + filters[filter_name] = filters[filter_name].lower() + + return filters diff --git a/trio2o/tests/unit/common/test_client.py b/trio2o/tests/unit/common/test_client.py index 736fc64..819dd1a 100644 --- a/trio2o/tests/unit/common/test_client.py +++ b/trio2o/tests/unit/common/test_client.py @@ -223,19 +223,6 @@ class ClientTest(unittest.TestCase): FAKE_RESOURCE, self.context, []) self.assertEqual(resources, [{'name': 'res1'}, {'name': 'res2'}]) - def test_list_endpoint_not_unique(self): - # add a new configuration with same pod and service type - config_dict = { - 'service_id': FAKE_SERVICE_ID + '_new', - 'pod_id': FAKE_SITE_ID, - 'service_type': FAKE_TYPE, - 'service_url': FAKE_URL - } - api.create_pod_service_configuration(self.context, config_dict) - self.assertRaises(exceptions.EndpointNotUnique, - self.client.list_resources, - FAKE_RESOURCE, self.context, []) - def test_list_endpoint_not_valid(self): cfg.CONF.set_override(name='auto_refresh_endpoint', override=False, group='client')