From b5e81f217ca73215e4b019c1c060bd6e8bd834ad Mon Sep 17 00:00:00 2001 From: Yaroslav Lobankov Date: Sat, 30 Jan 2016 21:27:02 -0600 Subject: [PATCH] [Verify] Don't create new image when image already exists The current Tempest resources context creates an image in the cloud every time when we run the tests and even when we run only one test. It is not optimal. In order to avoid creating the image every time a new option for `rally verify start` was added. So if we want to run tests, but we want to use some existing image, we should execute the following command: $ rally verify start --discover This option will tell Rally to search for the image with name that is configured in the rally.conf file and the discovered image will be used for the tests. The next step will be to add the same functionality for flavors. Change-Id: I0400d5451585ca3c1a82450a58b33125a2488b42 --- etc/rally.bash_completion | 2 +- etc/rally/rally.conf.sample | 13 ++++++-- rally/verification/tempest/config.py | 44 ++++++++++++++++++++++---- rally/verification/tempest/tempest.py | 4 +-- tests/unit/verification/test_config.py | 12 +++++-- 5 files changed, 61 insertions(+), 14 deletions(-) diff --git a/etc/rally.bash_completion b/etc/rally.bash_completion index 8d7a882d61..38dc4842f1 100644 --- a/etc/rally.bash_completion +++ b/etc/rally.bash_completion @@ -85,4 +85,4 @@ _rally() return 0 } -complete -o filenames -F _rally rally +complete -o filenames -F _rally rally \ No newline at end of file diff --git a/etc/rally/rally.conf.sample b/etc/rally/rally.conf.sample index ad26e55434..9cd70878ea 100644 --- a/etc/rally/rally.conf.sample +++ b/etc/rally/rally.conf.sample @@ -657,12 +657,21 @@ # CirrOS image URL (string value) #cirros_img_url = http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img -# Image disk format (string value) +# Image disk format to use when creating the image (string value) #disk_format = qcow2 -# Image container formate (string value) +# Image container format to use when creating the image (string value) #container_format = bare +# Regular expression for name of an image to discover it in the cloud +# and use it for the tests. Note that when Rally is searching for the +# image, case insensitive matching is performed. Specify nothing +# ('name_regex =') if you want to disable discovering. In this case +# Rally will create needed resources by itself if the values for the +# corresponding config options are not specified in the Tempest config +# file (string value) +#name_regex = ^.*(cirros|testvm).*$ + [role] diff --git a/rally/verification/tempest/config.py b/rally/verification/tempest/config.py index 277064520a..1243cee5ce 100644 --- a/rally/verification/tempest/config.py +++ b/rally/verification/tempest/config.py @@ -15,6 +15,7 @@ import inspect import os +import re from oslo_config import cfg import requests @@ -40,10 +41,20 @@ IMAGE_OPTS = [ help="CirrOS image URL"), cfg.StrOpt("disk_format", default="qcow2", - help="Image disk format"), + help="Image disk format to use when creating the image"), cfg.StrOpt("container_format", default="bare", - help="Image container formate") + help="Image container format to use when creating the image"), + cfg.StrOpt("name_regex", + default="^.*(cirros|testvm).*$", + help="Regular expression for name of an image to discover it " + "in the cloud and use it for the tests. Note that when " + "Rally is searching for the image, case insensitive " + "matching is performed. Specify nothing ('name_regex =') " + "if you want to disable discovering. In this case Rally " + "will create needed resources by itself if the values " + "for the corresponding config options are not specified " + "in the Tempest config file") ] ROLE_OPTS = [ @@ -97,9 +108,9 @@ class TempestConfig(utils.RandomNameGeneratorMixin): self.conf = configparser.ConfigParser() self.conf.read(os.path.join(os.path.dirname(__file__), "config.ini")) + self.image_name = parse.urlparse( CONF.image.cirros_img_url).path.split("/")[-1] - self._download_cirros_image() def _download_cirros_image(self): @@ -311,6 +322,7 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin): self.conf_path = conf_path self.conf = configparser.ConfigParser() self.conf.read(conf_path) + self.image_name = parse.urlparse( CONF.image.cirros_img_url).path.split("/")[-1] @@ -321,8 +333,10 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin): def __enter__(self): self._create_tempest_roles() - self._configure_option("compute", "image_ref", self._create_image) - self._configure_option("compute", "image_ref_alt", self._create_image) + self._configure_option("compute", "image_ref", + self._discover_or_create_image) + self._configure_option("compute", "image_ref_alt", + self._discover_or_create_image) self._configure_option("compute", "flavor_ref", self._create_flavor, 64) self._configure_option("compute", @@ -387,12 +401,28 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin): LOG.debug("Option '{opt}' is configured. " "{opt} = {value}".format(opt=option, value=value)) else: - LOG.debug("Option '{opt}' was configured manually " + LOG.debug("Option '{opt}' is already configured " "in Tempest config file. {opt} = {opt_val}" .format(opt=option, opt_val=option_value)) - def _create_image(self): + def _discover_or_create_image(self): glanceclient = self.clients.glance() + + if CONF.image.name_regex: + LOG.debug("Trying to discover an image with name matching " + "regular expression '%s'. Note that case insensitive " + "matching is performed" % CONF.image.name_regex) + img_list = [img for img in glanceclient.images.list() + if img.status.lower() == "active" and img.name] + for img in img_list: + if re.match(CONF.image.name_regex, img.name, re.IGNORECASE): + LOG.debug("The following image discovered: '{0}'. Using " + "image '{0}' for the tests".format(img.name)) + return img + + LOG.debug("There is no image with name matching " + "regular expression '%s'" % CONF.image.name_regex) + params = { "name": self.generate_random_name(), "disk_format": CONF.image.disk_format, diff --git a/rally/verification/tempest/tempest.py b/rally/verification/tempest/tempest.py index caa8994925..54302817b6 100644 --- a/rally/verification/tempest/tempest.py +++ b/rally/verification/tempest/tempest.py @@ -344,8 +344,8 @@ class Tempest(object): "testr_args": testr_args, "log_file": log_file or self.log_file_raw }) - # Create all resources needed for Tempest before running tests. - # Once tests finish, all created resources will be deleted. + # Discover or create all resources needed for Tempest before running + # tests. Once tests finish, all created resources will be deleted. with config.TempestResourcesContext( self.deployment, self.verification, self.config_file): # Run tests diff --git a/tests/unit/verification/test_config.py b/tests/unit/verification/test_config.py index edfa472bf9..73b3eae1dd 100644 --- a/tests/unit/verification/test_config.py +++ b/tests/unit/verification/test_config.py @@ -396,12 +396,20 @@ class TempestResourcesContextTestCase(test.TestCase): result = self.context.conf.get("compute", "flavor_ref") self.assertEqual("id1", result) + def test__discover_or_create_image_when_image_exists(self): + client = self.context.clients.glance() + client.images.list.return_value = [fakes.FakeResource(name="CirrOS", + status="active")] + image = self.context._discover_or_create_image() + self.assertEqual("CirrOS", image.name) + self.assertEqual(0, len(self.context._created_images)) + @mock.patch("six.moves.builtins.open") - def test__create_image(self, mock_open): + def test__discover_or_create_image(self, mock_open): client = self.context.clients.glance() client.images.create.side_effect = [fakes.FakeImage(id="id1")] - image = self.context._create_image() + image = self.context._discover_or_create_image() self.assertEqual("id1", image.id) self.assertEqual("id1", self.context._created_images[0].id)