[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
This commit is contained in:
Yaroslav Lobankov 2016-01-30 21:27:02 -06:00
parent 115e66cf24
commit b5e81f217c
5 changed files with 61 additions and 14 deletions

View File

@ -657,12 +657,21 @@
# CirrOS image URL (string value) # CirrOS image URL (string value)
#cirros_img_url = http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img #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 #disk_format = qcow2
# Image container formate (string value) # Image container format to use when creating the image (string value)
#container_format = bare #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] [role]

View File

@ -15,6 +15,7 @@
import inspect import inspect
import os import os
import re
from oslo_config import cfg from oslo_config import cfg
import requests import requests
@ -40,10 +41,20 @@ IMAGE_OPTS = [
help="CirrOS image URL"), help="CirrOS image URL"),
cfg.StrOpt("disk_format", cfg.StrOpt("disk_format",
default="qcow2", default="qcow2",
help="Image disk format"), help="Image disk format to use when creating the image"),
cfg.StrOpt("container_format", cfg.StrOpt("container_format",
default="bare", 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 = [ ROLE_OPTS = [
@ -97,9 +108,9 @@ class TempestConfig(utils.RandomNameGeneratorMixin):
self.conf = configparser.ConfigParser() self.conf = configparser.ConfigParser()
self.conf.read(os.path.join(os.path.dirname(__file__), "config.ini")) self.conf.read(os.path.join(os.path.dirname(__file__), "config.ini"))
self.image_name = parse.urlparse( self.image_name = parse.urlparse(
CONF.image.cirros_img_url).path.split("/")[-1] CONF.image.cirros_img_url).path.split("/")[-1]
self._download_cirros_image() self._download_cirros_image()
def _download_cirros_image(self): def _download_cirros_image(self):
@ -311,6 +322,7 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin):
self.conf_path = conf_path self.conf_path = conf_path
self.conf = configparser.ConfigParser() self.conf = configparser.ConfigParser()
self.conf.read(conf_path) self.conf.read(conf_path)
self.image_name = parse.urlparse( self.image_name = parse.urlparse(
CONF.image.cirros_img_url).path.split("/")[-1] CONF.image.cirros_img_url).path.split("/")[-1]
@ -321,8 +333,10 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin):
def __enter__(self): def __enter__(self):
self._create_tempest_roles() self._create_tempest_roles()
self._configure_option("compute", "image_ref", self._create_image) self._configure_option("compute", "image_ref",
self._configure_option("compute", "image_ref_alt", self._create_image) self._discover_or_create_image)
self._configure_option("compute", "image_ref_alt",
self._discover_or_create_image)
self._configure_option("compute", self._configure_option("compute",
"flavor_ref", self._create_flavor, 64) "flavor_ref", self._create_flavor, 64)
self._configure_option("compute", self._configure_option("compute",
@ -387,12 +401,28 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin):
LOG.debug("Option '{opt}' is configured. " LOG.debug("Option '{opt}' is configured. "
"{opt} = {value}".format(opt=option, value=value)) "{opt} = {value}".format(opt=option, value=value))
else: else:
LOG.debug("Option '{opt}' was configured manually " LOG.debug("Option '{opt}' is already configured "
"in Tempest config file. {opt} = {opt_val}" "in Tempest config file. {opt} = {opt_val}"
.format(opt=option, opt_val=option_value)) .format(opt=option, opt_val=option_value))
def _create_image(self): def _discover_or_create_image(self):
glanceclient = self.clients.glance() 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 = { params = {
"name": self.generate_random_name(), "name": self.generate_random_name(),
"disk_format": CONF.image.disk_format, "disk_format": CONF.image.disk_format,

View File

@ -344,8 +344,8 @@ class Tempest(object):
"testr_args": testr_args, "testr_args": testr_args,
"log_file": log_file or self.log_file_raw "log_file": log_file or self.log_file_raw
}) })
# Create all resources needed for Tempest before running tests. # Discover or create all resources needed for Tempest before running
# Once tests finish, all created resources will be deleted. # tests. Once tests finish, all created resources will be deleted.
with config.TempestResourcesContext( with config.TempestResourcesContext(
self.deployment, self.verification, self.config_file): self.deployment, self.verification, self.config_file):
# Run tests # Run tests

View File

@ -396,12 +396,20 @@ class TempestResourcesContextTestCase(test.TestCase):
result = self.context.conf.get("compute", "flavor_ref") result = self.context.conf.get("compute", "flavor_ref")
self.assertEqual("id1", result) 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") @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 = self.context.clients.glance()
client.images.create.side_effect = [fakes.FakeImage(id="id1")] 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", image.id)
self.assertEqual("id1", self.context._created_images[0].id) self.assertEqual("id1", self.context._created_images[0].id)