From 3976fdc1cdfb6e0e749a6886a732040cb7dc6ece Mon Sep 17 00:00:00 2001 From: Shobhi Jain Date: Wed, 11 Apr 2018 09:34:22 +0100 Subject: Replace glance create image with shade client. Function create_image now uses shade client. JIRA: YARDSTICK-892 Change-Id: Ia41d9ce702a1f24031080f8a365c1b2bd9ac9faa Signed-off-by: Shobhi Jain (cherry picked from commit c31edc5ab9013d93ffd32ed72adb14740e43b24a) --- yardstick/benchmark/scenarios/lib/create_image.py | 71 ++++++++++---------- yardstick/common/exceptions.py | 4 ++ yardstick/common/openstack_utils.py | 76 +++++++++++++++------- .../benchmark/scenarios/lib/test_create_image.py | 62 ++++++++++++------ .../tests/unit/common/test_openstack_utils.py | 36 ++++++++++ 5 files changed, 171 insertions(+), 78 deletions(-) diff --git a/yardstick/benchmark/scenarios/lib/create_image.py b/yardstick/benchmark/scenarios/lib/create_image.py index bcffc7452..d057894a9 100644 --- a/yardstick/benchmark/scenarios/lib/create_image.py +++ b/yardstick/benchmark/scenarios/lib/create_image.py @@ -6,14 +6,11 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## - -from __future__ import print_function -from __future__ import absolute_import - import logging from yardstick.benchmark.scenarios import base -import yardstick.common.openstack_utils as op_utils +from yardstick.common import openstack_utils +from yardstick.common import exceptions LOG = logging.getLogger(__name__) @@ -26,20 +23,23 @@ class CreateImage(base.Scenario): def __init__(self, scenario_cfg, context_cfg): self.scenario_cfg = scenario_cfg self.context_cfg = context_cfg - self.options = self.scenario_cfg['options'] - - self.image_name = self.options.get("image_name", "TestImage") - self.file_path = self.options.get("file_path", None) - self.disk_format = self.options.get("disk_format", "qcow2") - self.container_format = self.options.get("container_format", "bare") - self.min_disk = self.options.get("min_disk", 0) - self.min_ram = self.options.get("min_ram", 0) - self.protected = self.options.get("protected", False) - self.public = self.options.get("public", "public") - self.tags = self.options.get("tags", []) - self.custom_property = self.options.get("property", {}) - - self.glance_client = op_utils.get_glance_client() + self.options = self.scenario_cfg["options"] + + self.name = self.options["image_name"] + self.file_name = self.options.get("file_name") + self.container = self.options.get("container", 'images') + self.md5 = self.options.get("md5") + self.sha256 = self.options.get("sha256") + self.disk_format = self.options.get("disk_format") + self.container_format = self.options.get("container_format",) + self.disable_vendor_agent = self.options.get("disable_vendor_agent", True) + self.wait = self.options.get("wait", True) + self.timeout = self.options.get("timeout", 3600) + self.allow_duplicates = self.options.get("allow_duplicates", False) + self.meta = self.options.get("meta") + self.volume = self.options.get("volume") + + self.shade_client = openstack_utils.get_shade_client() self.setup_done = False @@ -54,19 +54,22 @@ class CreateImage(base.Scenario): if not self.setup_done: self.setup() - image_id = op_utils.create_image(self.glance_client, self.image_name, - self.file_path, self.disk_format, - self.container_format, self.min_disk, - self.min_ram, self.protected, self.tags, - self.public, **self.custom_property) - - if image_id: - LOG.info("Create image successful!") - values = [image_id] - - else: - LOG.info("Create image failed!") - values = [] - - keys = self.scenario_cfg.get('output', '').split() + image_id = openstack_utils.create_image( + self.shade_client, self.name, filename=self.file_name, + container=self.container, md5=self.md5, sha256=self.sha256, + disk_format=self.disk_format, + container_format=self.container_format, + disable_vendor_agent=self.disable_vendor_agent, wait=self.wait, + timeout=self.timeout, allow_duplicates=self.allow_duplicates, + meta=self.meta, volume=self.volume) + + if not image_id: + result.update({"image_create": 0}) + LOG.error("Create image failed!") + raise exceptions.ScenarioCreateImageError + + result.update({"image_create": 1}) + LOG.info("Create image successful!") + keys = self.scenario_cfg.get("output", '').split() + values = [image_id] return self._push_to_outputs(keys, values) diff --git a/yardstick/common/exceptions.py b/yardstick/common/exceptions.py index 39a3d2004..faa8688d8 100644 --- a/yardstick/common/exceptions.py +++ b/yardstick/common/exceptions.py @@ -270,6 +270,10 @@ class UpdateOpenrcError(ApiServerError): message = 'Update openrc ERROR!' +class ScenarioCreateImageError(YardstickException): + message = 'Glance Create Image Scenario failed' + + class IxNetworkClientNotConnected(YardstickException): message = 'IxNetwork client not connected to a TCL server' diff --git a/yardstick/common/openstack_utils.py b/yardstick/common/openstack_utils.py index a2b75e0e0..56cd4958e 100644 --- a/yardstick/common/openstack_utils.py +++ b/yardstick/common/openstack_utils.py @@ -714,32 +714,62 @@ def get_image_id(glance_client, image_name): # pragma: no cover return next((i.id for i in images if i.name == image_name), None) -def create_image(glance_client, image_name, file_path, disk_format, - container_format, min_disk, min_ram, protected, tag, - public, **kwargs): # pragma: no cover - if not os.path.isfile(file_path): - log.error("Error: file %s does not exist.", file_path) - return None +def create_image(shade_client, name, filename=None, container='images', + md5=None, sha256=None, disk_format=None, + container_format=None, disable_vendor_agent=True, + wait=False, timeout=3600, allow_duplicates=False, meta=None, + volume=None, **kwargs): + """Upload an image. + + :param name:(str) Name of the image to create. If it is a pathname of an + image, the name will be constructed from the extensionless + basename of the path. + :param filename:(str) The path to the file to upload, if needed. + :param container:(str) Name of the container in swift where images should + be uploaded for import if the cloud requires such a thing. + :param md5:(str) md5 sum of the image file. If not given, an md5 will + be calculated. + :param sha256:(str) sha256 sum of the image file. If not given, an md5 + will be calculated. + :param disk_format:(str) The disk format the image is in. + :param container_format:(str) The container format the image is in. + :param disable_vendor_agent:(bool) Whether or not to append metadata + flags to the image to inform the cloud in + question to not expect a vendor agent to be running. + :param wait:(bool) If true, waits for image to be created. + :param timeout:(str) Seconds to wait for image creation. + :param allow_duplicates:(bool) If true, skips checks that enforce unique + image name. + :param meta:(dict) A dict of key/value pairs to use for metadata that + bypasses automatic type conversion. + :param volume:(str) Name or ID or volume object of a volume to create an + image from. + Additional kwargs will be passed to the image creation as additional + metadata for the image and will have all values converted to string + except for min_disk, min_ram, size and virtual_size which will be + converted to int. + If you are sure you have all of your data types correct or have an + advanced need to be explicit, use meta. If you are just a normal + consumer, using kwargs is likely the right choice. + If a value is in meta and kwargs, meta wins. + :returns: Image id + """ try: - image_id = get_image_id(glance_client, image_name) + image_id = shade_client.get_image_id(name) if image_id is not None: - log.info("Image %s already exists.", image_name) - else: - log.info("Creating image '%s' from '%s'...", image_name, file_path) - - image = glance_client.images.create( - name=image_name, visibility=public, disk_format=disk_format, - container_format=container_format, min_disk=min_disk, - min_ram=min_ram, tags=tag, protected=protected, **kwargs) - image_id = image.id - with open(file_path) as image_data: - glance_client.images.upload(image_id, image_data) + log.info("Image %s already exists.", name) + return image_id + log.info("Creating image '%s'", name) + image = shade_client.create_image( + name, filename=filename, container=container, md5=md5, sha256=sha256, + disk_format=disk_format, container_format=container_format, + disable_vendor_agent=disable_vendor_agent, wait=wait, timeout=timeout, + allow_duplicates=allow_duplicates, meta=meta, volume=volume, **kwargs) + image_id = image["id"] return image_id - except Exception: # pylint: disable=broad-except - log.error( - "Error [create_glance_image(glance_client, '%s', '%s', '%s')]", - image_name, file_path, public) - return None + except exc.OpenStackCloudException as op_exc: + log.error("Failed to create_image(shade_client). " + "Exception message: %s", op_exc.orig_message) def delete_image(glance_client, image_id): # pragma: no cover diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py index 639cf2906..aebd1dfe8 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py @@ -6,30 +6,50 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -import unittest + import mock +from oslo_utils import uuidutils +import unittest -from yardstick.benchmark.scenarios.lib import create_image from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_image + -# NOTE(elfoley): There should be more tests here. class CreateImageTestCase(unittest.TestCase): - @mock.patch.object(openstack_utils, 'create_image') - @mock.patch.object(openstack_utils, 'get_glance_client') - def test_create_image(self, mock_get_glance_client, mock_create_image): - options = { - 'image_name': 'yardstick_test_image_01', - 'disk_format': 'qcow2', - 'container_format': 'bare', - 'min_disk': '1', - 'min_ram': '512', - 'protected': 'False', - 'tags': '["yardstick automatic test image"]', - 'file_path': '/home/opnfv/images/cirros-0.3.5-x86_64-disk.img' - } - args = {"options": options} - obj = create_image.CreateImage(args, {}) - obj.run({}) - mock_create_image.assert_called_once() - mock_get_glance_client.assert_called_once() + def setUp(self): + self._mock_create_image = mock.patch.object( + openstack_utils, 'create_image') + self.mock_create_image = ( + self._mock_create_image.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_image, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'image_name': 'yardstick_image'}} + self.result = {} + self.cimage_obj = create_image.CreateImage(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_image.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self.cimage_obj.scenario_cfg = {'output': 'id'} + self.mock_create_image.return_value = _uuid + output = self.cimage_obj.run(self.result) + self.assertEqual({'image_create': 1}, self.result) + self.assertEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create image successful!') + + def test_run_fail(self): + self.mock_create_image.return_value = None + with self.assertRaises(exceptions.ScenarioCreateImageError): + self.cimage_obj.run(self.result) + self.assertEqual({'image_create': 0}, self.result) + self.mock_log.error.assert_called_once_with('Create image failed!') diff --git a/yardstick/tests/unit/common/test_openstack_utils.py b/yardstick/tests/unit/common/test_openstack_utils.py index d1d91e2d5..9b23c65b8 100644 --- a/yardstick/tests/unit/common/test_openstack_utils.py +++ b/yardstick/tests/unit/common/test_openstack_utils.py @@ -631,3 +631,39 @@ class DetachVolumeTestCase(unittest.TestCase): 'volume_name_or_id') mock_logger.error.assert_called_once() self.assertFalse(output) + + +# ********************************************* +# GLANCE +# ********************************************* + +class CreateImageTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self._uuid = uuidutils.generate_uuid() + self.name = 'image_name' + + @mock.patch.object(openstack_utils, 'log') + def test_create_image_already_exit(self, mock_logger): + self.mock_shade_client.get_image_id.return_value = self._uuid + output = openstack_utils.create_image(self.mock_shade_client, self.name) + mock_logger.info.assert_called_once() + self.assertEqual(self._uuid, output) + + def test_create_image(self): + self.mock_shade_client.get_image_id.return_value = None + self.mock_shade_client.create_image.return_value = {'id': self._uuid} + output = openstack_utils.create_image(self.mock_shade_client, self.name) + self.assertEqual(self._uuid, output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_image_exception(self, mock_logger): + self.mock_shade_client.get_image_id.return_value = None + self.mock_shade_client.create_image.side_effect = ( + exc.OpenStackCloudException('error message')) + + output = openstack_utils.create_image(self.mock_shade_client, + self.name) + mock_logger.error.assert_called_once() + self.assertIsNone(output) -- cgit 1.2.3-korg