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)