From 895e206d89f8771c870b73c51b132ee5decd7857 Mon Sep 17 00:00:00 2001 From: chenjiankun Date: Tue, 3 Apr 2018 06:23:42 +0000 Subject: Bugfix: Can't get image list in API JIRA: YARDSTICK-1110 To match OpenStack Pike release, we upgrade novaclient from 7.1.1 to 9.1.1. And the client interface has changed, it no longer has nova_client.images attribute. So I use shade instead. Later will change all same issue. Change-Id: Ie4f54069d4346e44e2ad925439930504b945cbad Signed-off-by: chenjiankun (cherry picked from commit 5ac59e495d74b64ab377f1ba25bb00b11da14e67) --- api/resources/v2/images.py | 69 ++++++++-------------- yardstick/common/openstack_utils.py | 14 ++++- yardstick/common/utils.py | 5 ++ .../tests/unit/apiserver/resources/v2/__init__.py | 0 .../unit/apiserver/resources/v2/test_images.py | 46 +++++++++++++++ .../tests/unit/common/test_openstack_utils.py | 18 ++++++ 6 files changed, 105 insertions(+), 47 deletions(-) create mode 100644 yardstick/tests/unit/apiserver/resources/v2/__init__.py create mode 100644 yardstick/tests/unit/apiserver/resources/v2/test_images.py diff --git a/api/resources/v2/images.py b/api/resources/v2/images.py index 0c36a0a26..c3e5ee73e 100644 --- a/api/resources/v2/images.py +++ b/api/resources/v2/images.py @@ -18,8 +18,7 @@ from api.database.v2.handlers import V2ImageHandler from api.database.v2.handlers import V2EnvironmentHandler from yardstick.common.utils import result_handler from yardstick.common.utils import source_env -from yardstick.common.utils import change_obj_to_dict -from yardstick.common.openstack_utils import get_nova_client +from yardstick.common import openstack_utils from yardstick.common.openstack_utils import get_glance_client from yardstick.common import constants as consts @@ -47,39 +46,21 @@ class V2Images(ApiResource): def get(self): try: source_env(consts.OPENRC) - except Exception: + except OSError: return result_handler(consts.API_ERROR, 'source openrc error') - nova_client = get_nova_client() - try: - images_list = nova_client.images.list() - except Exception: + image_list = openstack_utils.list_images() + + if image_list is False: return result_handler(consts.API_ERROR, 'get images error') - else: - images = {i.name: self.get_info(change_obj_to_dict(i)) for i in images_list} + + images = {i.name: format_image_info(i) for i in image_list} return result_handler(consts.API_SUCCESS, {'status': 1, 'images': images}) def post(self): return self._dispatch_post() - def get_info(self, data): - try: - size = data['OS-EXT-IMG-SIZE:size'] - except KeyError: - size = None - else: - size = float(size) / 1024 / 1024 - - result = { - 'name': data.get('name', ''), - 'discription': data.get('description', ''), - 'size': size, - 'status': data.get('status'), - 'time': data.get('updated') - } - return result - def load_image(self, args): try: image_name = args['name'] @@ -268,7 +249,7 @@ class V2Images(ApiResource): r = requests.head(url) try: file_size = int(r.headers['content-length']) - except Exception: + except (TypeError, ValueError): return with open(path, 'wb') as f: @@ -303,14 +284,13 @@ class V2Image(ApiResource): except ValueError: return result_handler(consts.API_ERROR, 'no such image id') - nova_client = get_nova_client() - images = nova_client.images.list() + images = openstack_utils.list_images() try: image = next((i for i in images if i.name == image.name)) except StopIteration: pass - return_image = self.get_info(change_obj_to_dict(image)) + return_image = format_image_info(image) return_image['id'] = image_id return result_handler(consts.API_SUCCESS, {'image': return_image}) @@ -349,19 +329,16 @@ class V2Image(ApiResource): return result_handler(consts.API_SUCCESS, {'image': image_id}) - def get_info(self, data): - try: - size = data['OS-EXT-IMG-SIZE:size'] - except KeyError: - size = None - else: - size = float(size) / 1024 / 1024 - - result = { - 'name': data.get('name', ''), - 'description': data.get('description', ''), - 'size': size, - 'status': data.get('status'), - 'time': data.get('updated') - } - return result + +def format_image_info(image): + image_dict = {} + + if image is None: + return image_dict + + image_dict['name'] = image.name + image_dict['size'] = float(image.size) / 1024 / 1024 + image_dict['status'] = image.status.upper() + image_dict['time'] = image.updated_at + + return image_dict diff --git a/yardstick/common/openstack_utils.py b/yardstick/common/openstack_utils.py index 2785230c0..5208a2749 100644 --- a/yardstick/common/openstack_utils.py +++ b/yardstick/common/openstack_utils.py @@ -733,7 +733,7 @@ def create_security_group_full(neutron_client, sg_name, log.debug("Adding ICMP rules in security group '%s'...", sg_name) if not create_security_group_rule(neutron_client, sg_id, - 'ingress', 'icmp'): + 'ingress', 'icmp'): log.error("Failed to create the security group rule...") return None @@ -797,6 +797,18 @@ def delete_image(glance_client, image_id): # pragma: no cover return True +def list_images(shade_client=None): + if shade_client is None: + shade_client = get_shade_client() + + try: + return shade_client.list_images() + except exc.OpenStackCloudException as o_exc: + log.error("Error [list_images(shade_client)]." + "Exception message, '%s'", o_exc.orig_message) + return False + + # ********************************************* # CINDER # ********************************************* diff --git a/yardstick/common/utils.py b/yardstick/common/utils.py index 357f66be8..44cc92a7c 100644 --- a/yardstick/common/utils.py +++ b/yardstick/common/utils.py @@ -136,6 +136,11 @@ def source_env(env_file): p = subprocess.Popen(". %s; env" % env_file, stdout=subprocess.PIPE, shell=True) output = p.communicate()[0] + + # sometimes output type would be binary_type, and it don't have splitlines + # method, so we need to decode + if isinstance(output, six.binary_type): + output = encodeutils.safe_decode(output) env = dict(line.split('=', 1) for line in output.splitlines() if '=' in line) os.environ.update(env) return env diff --git a/yardstick/tests/unit/apiserver/resources/v2/__init__.py b/yardstick/tests/unit/apiserver/resources/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/yardstick/tests/unit/apiserver/resources/v2/test_images.py b/yardstick/tests/unit/apiserver/resources/v2/test_images.py new file mode 100644 index 000000000..ab131eec5 --- /dev/null +++ b/yardstick/tests/unit/apiserver/resources/v2/test_images.py @@ -0,0 +1,46 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import mock + +import unittest + +from yardstick.tests.unit.apiserver import APITestCase +from api.resources.v2.images import format_image_info + + +class V2ImagesTestCase(APITestCase): + @mock.patch('yardstick.common.openstack_utils.list_images') + @mock.patch('yardstick.common.utils.source_env') + def test_get(self, _, mock_list_images): + if self.app is None: + unittest.skip('host config error') + return + + single_image = mock.MagicMock() + single_image.name = 'yardstick-image' + single_image.size = 16384 + single_image.status = 'active' + single_image.updated_at = '2018-04-08' + + mock_list_images.return_value = [single_image] + url = 'api/v2/yardstick/images' + resp = self._get(url) + self.assertEqual(resp.get('status'), 1) + + +class FormatImageInfoTestCase(unittest.TestCase): + def test_format_image_info(self): + image = mock.MagicMock() + image.name = 'yardstick-image' + image.size = 1048576 + image.status = 'active' + image.updated_at = '2018-04-08' + + image_dict = format_image_info(image) + self.assertEqual(image_dict.get('size'), 1) diff --git a/yardstick/tests/unit/common/test_openstack_utils.py b/yardstick/tests/unit/common/test_openstack_utils.py index 3b7e8eaa1..5c7e5bfab 100644 --- a/yardstick/tests/unit/common/test_openstack_utils.py +++ b/yardstick/tests/unit/common/test_openstack_utils.py @@ -264,3 +264,21 @@ class CreateSecurityGroupRuleTestCase(unittest.TestCase): self.mock_shade_client, self.secgroup_name_or_id) mock_logger.error.assert_called_once() self.assertFalse(output) + + +class ListImageTestCase(unittest.TestCase): + + def test_list_images(self): + mock_shade_client = mock.MagicMock() + mock_shade_client.list_images.return_value = [] + openstack_utils.list_images(mock_shade_client) + + @mock.patch.object(openstack_utils, 'log') + def test_list_images_exception(self, mock_logger): + mock_shade_client = mock.MagicMock() + mock_shade_client.list_images = mock.MagicMock() + mock_shade_client.list_images.side_effect = ( + exc.OpenStackCloudException('error message')) + images = openstack_utils.list_images(mock_shade_client) + mock_logger.error.assert_called_once() + self.assertFalse(images) -- cgit 1.2.3-korg