Bugfix: Can't get image list in API 45/55845/1
authorchenjiankun <chenjiankun1@huawei.com>
Tue, 3 Apr 2018 06:23:42 +0000 (06:23 +0000)
committerJack Chan <chenjiankun1@huawei.com>
Tue, 17 Apr 2018 01:42:43 +0000 (01:42 +0000)
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 <chenjiankun1@huawei.com>
(cherry picked from commit 5ac59e495d74b64ab377f1ba25bb00b11da14e67)

api/resources/v2/images.py
yardstick/common/openstack_utils.py
yardstick/common/utils.py
yardstick/tests/unit/apiserver/resources/v2/__init__.py [new file with mode: 0644]
yardstick/tests/unit/apiserver/resources/v2/test_images.py [new file with mode: 0644]
yardstick/tests/unit/common/test_openstack_utils.py

index 0c36a0a..c3e5ee7 100644 (file)
@@ -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
index 2785230..5208a27 100644 (file)
@@ -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
 # *********************************************
index 357f66b..44cc92a 100644 (file)
@@ -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 (file)
index 0000000..e69de29
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 (file)
index 0000000..ab131ee
--- /dev/null
@@ -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)
index 3b7e8ea..5c7e5bf 100644 (file)
@@ -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)