From 12d4aa05012d6f7557e77d7495d6c290cc31ff03 Mon Sep 17 00:00:00 2001 From: Martin Kopec Date: Wed, 27 Mar 2019 13:11:18 +0000 Subject: [PATCH] Add option for converting images The review adds an option for converting images to raw format before they are uploaded to glance. In some cases the boot of the image is faster when the image is already in raw format. Story: 2005454 Task: 30506 Change-Id: Ie7226ecaad8feaf0a0c416905cf3e2107d4e8cd3 --- config_tempest/main.py | 7 +++- config_tempest/services/image.py | 41 +++++++++++++++++-- config_tempest/tests/services/test_image.py | 18 +++++++- doc/source/user/usage.rst | 18 ++++++++ ...or-converting-images-b3ea78ce1faf9df6.yaml | 7 ++++ 5 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/Add-argument-for-converting-images-b3ea78ce1faf9df6.yaml diff --git a/config_tempest/main.py b/config_tempest/main.py index 1ee27dd6..1ff7815f 100755 --- a/config_tempest/main.py +++ b/config_tempest/main.py @@ -312,6 +312,9 @@ def get_arg_parser(): parser.add_argument('--flavor-min-disk', default=C.DEFAULT_FLAVOR_DISK, type=int, help="""Specify minimum disk size for new flavours, default is '%s'.""" % C.DEFAULT_FLAVOR_DISK) + parser.add_argument('--convert-to-raw', action='store_true', default=False, + help="""Convert images to raw format before uploading + to glance.""") parser.add_argument('--network-id', help="""Specify which network with external connectivity should be used by the tests.""") @@ -528,7 +531,8 @@ def config_tempest(**kwargs): image.set_image_preferences(kwargs.get('image_disk_format', C.DEFAULT_IMAGE_FORMAT), kwargs.get('non_admin', False), - no_rng=kwargs.get('no_rng', False)) + no_rng=kwargs.get('no_rng', False), + convert=kwargs.get('convert_to_raw', False)) image.create_tempest_images(conf) has_neutron = services.is_service("network") @@ -573,6 +577,7 @@ def main(): config_tempest( append=args.append, cloud_creds=cloud_creds, + convert_to_raw=args.convert_to_raw, create=args.create, create_accounts_file=args.create_accounts_file, debug=args.debug, diff --git a/config_tempest/services/image.py b/config_tempest/services/image.py index 18c7443a..12345876 100644 --- a/config_tempest/services/image.py +++ b/config_tempest/services/image.py @@ -15,6 +15,7 @@ import os import shutil +import subprocess from six.moves import urllib from tempest.lib import exceptions @@ -31,15 +32,19 @@ class ImageService(VersionedService): disable_ssl_validation, client) - def set_image_preferences(self, disk_format, non_admin, no_rng=False): + def set_image_preferences(self, disk_format, non_admin, no_rng=False, + convert=False): """Sets image prefferences. :type disk_format: string :type non_admin: bool + :type no_rng: bool + :type convert: bool """ self.disk_format = disk_format self.non_admin = non_admin self.no_rng = no_rng + self.convert = convert def set_default_tempest_options(self, conf): # When cirros is the image, set validation.image_ssh_user to cirros. @@ -90,12 +95,13 @@ class ImageService(VersionedService): img_path = os.path.join(img_dir, os.path.basename(image_path)) name = image_path[image_path.rfind('/') + 1:] + if self.convert and name[-4:] == ".img": + name = name[:-4] + ".raw" if not os.path.exists(img_dir): try: os.makedirs(img_dir) except OSError: raise - conf.set('scenario', 'img_file', name) alt_name = name + "_alt" image_id = None if conf.has_option('compute', 'image_ref'): @@ -109,7 +115,9 @@ class ImageService(VersionedService): alt_image_id = self.find_or_upload_image(alt_image_id, alt_name, image_source=image_path, image_dest=img_path) - + # get name of the image_id + image_id_name = self._find_image(image_id, '')['name'] + conf.set('scenario', 'img_file', image_id_name) conf.set('compute', 'image_ref', image_id) conf.set('compute', 'image_ref_alt', alt_image_id) @@ -182,6 +190,9 @@ class ImageService(VersionedService): :type name: string :type path: string """ + if self.convert: + path = self.convert_image_to_raw(path) + C.LOG.info("Uploading image '%s' from '%s'", name, os.path.abspath(path)) if self.non_admin: @@ -225,3 +236,27 @@ class ImageService(VersionedService): data = f.read() with open(destination, "wb") as dest: dest.write(data) + + def convert_image_to_raw(self, path): + """Converts given image to raw format. + + :type path: string + :return: path of the converted image + :rtype: string + """ + head, tail = os.path.split(path) + name = tail.rsplit('.', 1)[0] + '.raw' + raw_path = os.path.join(head, name) + # check if converted already + if os.path.exists(raw_path): + C.LOG.info("Image already converted in '%s'.", raw_path) + else: + C.LOG.info("Converting image '%s' to '%s'", + os.path.abspath(path), os.path.abspath(raw_path)) + rc = subprocess.call(['qemu-img', 'convert', path, raw_path]) + if rc != 0: + raise Exception("Converting of the image has finished with " + "non-zero return code. The return code was " + "'%d'", rc) + self.disk_format = 'raw' + return raw_path diff --git a/config_tempest/tests/services/test_image.py b/config_tempest/tests/services/test_image.py index 5250a613..dcf7c7e4 100644 --- a/config_tempest/tests/services/test_image.py +++ b/config_tempest/tests/services/test_image.py @@ -38,6 +38,7 @@ class TestImageService(BaseServiceTest): disable_ssl_validation=False) self.Service.disk_format = ".format" self.Service.non_admin = False + self.Service.convert = False self.Service.client = self.FakeServiceClient() self.dir = "/img/" @@ -46,11 +47,14 @@ class TestImageService(BaseServiceTest): self.conf.set("image", "image_path", "my_image.qcow2") self.conf.set("image", "http_image", "http_image.qcow2") + @mock.patch('config_tempest.services.image.ImageService._find_image') @mock.patch('config_tempest.services.image.ImageService' '.find_or_upload_image') @mock.patch('os.makedirs') - def _test_create_tempest_images(self, mock_makedirs, mock_find_upload): + def _test_create_tempest_images(self, mock_makedirs, mock_find_upload, + mock_find_image): mock_find_upload.side_effect = ["id_c", "id_d"] + mock_find_image.return_value = {'name': 'my_image.qcow2'} self.Service.create_tempest_images(conf=self.conf) mock_makedirs.assert_called() self.assertEqual(self.conf.get('compute', 'image_ref'), 'id_c') @@ -195,3 +199,15 @@ class TestImageService(BaseServiceTest): resp = self.Service._find_image(image_id="001", image_name="cirros") self.assertEqual(resp, expected_resp) + + @mock.patch('subprocess.call') + @mock.patch('os.path.exists') + def test_convert_image_to_raw(self, mock_exists, mock_subcall): + path = '/path/of/my/image.qcow2' + raw_path = '/path/of/my/image.raw' + mock_exists.return_value = False + mock_subcall.return_value = 0 + self.Service.convert_image_to_raw(path) + mock_subcall.assert_called_with(['qemu-img', 'convert', + path, raw_path]) + self.assertEqual(self.Service.disk_format, 'raw') diff --git a/doc/source/user/usage.rst b/doc/source/user/usage.rst index 140cdad8..2eac755c 100644 --- a/doc/source/user/usage.rst +++ b/doc/source/user/usage.rst @@ -401,6 +401,24 @@ image. .. _CLI options: ../cli/cli_options.html +Converting images to .raw format +******************************** + +By using ``--convert-to-raw`` argument you can make ``python-tempestconf`` +convert the image given by ``--image`` argument to **.raw** format before +uploading it to glance. If Ceph is used as a backend, the boot time of the +image will be faster when the image is already in **.raw** format. + +In the following example the ``/my/path/to/myImage.img`` image will be +downloaded, then converted to **.raw** format and then uploaded to glance. + +.. code-block:: shell-session + + $ discover-tempest-config \ + --os-cloud myCloud \ + --image /my/path/to/myImage.img \ + --convert-to-raw + Flavors +++++++ diff --git a/releasenotes/notes/Add-argument-for-converting-images-b3ea78ce1faf9df6.yaml b/releasenotes/notes/Add-argument-for-converting-images-b3ea78ce1faf9df6.yaml new file mode 100644 index 00000000..f66fce41 --- /dev/null +++ b/releasenotes/notes/Add-argument-for-converting-images-b3ea78ce1faf9df6.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + A new argument for converting images before uploading them to glance + is added. If the arugment is used, the images are converted to .raw format. + The feature will reduce boot time of the images in case ceph is the + backend.