[Verify] Add tempest configs and move to new section

Moved the old image and role options to the [tempest] section and added a
few flavor related config options. With these new options it is possible
to configure the RAM size of the two flavors used when running tempest
and the RAM size of the orchestration instance type flavor.

These were previously hardcoded to 64M, 128M and 64M respectively which
may cause the test image to fail under certain conditions, due to lack of
memory (e.g. AArch64 VMs seem to fall into this category).

After applying this fix, the three ram sizes and all other tempest related
options can be configured in the [tempest] section of /etc/rally/rally.conf.

Closes-Bug: #1580695

Change-Id: I33b8682310a42e66b408802a0d955fc886f6c8ed
Signed-off-by: Ciprian Barbu <ciprian.barbu@gmail.com>
This commit is contained in:
Ciprian Barbu 2016-05-11 20:05:32 +03:00 committed by Andrey Kurilin
parent 310248737b
commit 48b9975582
4 changed files with 124 additions and 87 deletions

View File

@ -6,10 +6,11 @@
# If set to true, the logging level will be set to DEBUG instead of
# the default INFO level. (boolean value)
# Note: This option can be changed without restarting.
#debug = false
# If set to false, the logging level will be set to WARNING instead of
# the default INFO level. (boolean value)
# DEPRECATED: If set to false, the logging level will be set to
# WARNING instead of the default INFO level. (boolean value)
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
#verbose = true
@ -155,13 +156,6 @@
# point value)
#glance_image_create_poll_interval = 1.0
# Time to wait for glance image to be deleted. (floating point value)
#glance_image_delete_timeout = 120.0
# Interval between checks when waiting for image deletion. (floating
# point value)
#glance_image_delete_poll_interval = 1.0
# Time(in sec) to sleep after creating a resource before polling for
# it status. (floating point value)
#heat_stack_create_prepoll_delay = 2.0
@ -635,51 +629,74 @@
#db_max_retries = 20
[image]
[roles_context]
#
# From rally
#
# CirrOS image URL (string value)
#cirros_img_url = http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img
# How many concurrent threads to use for serving roles context
# (integer value)
#resource_management_workers = 30
[tempest]
#
# From rally
#
# image URL (string value)
# Deprecated group/name - [image]/cirros_img_url
#img_url = http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img
# Image disk format to use when creating the image (string value)
#disk_format = qcow2
# Deprecated group/name - [image]/disk_format
#img_disk_format = qcow2
# Image container format to use when creating the image (string value)
#container_format = bare
# Deprecated group/name - [image]/container_format
#img_container_format = bare
# Regular expression for name of a public 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]
#
# From rally
#
# nothing ('img_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)
# Deprecated group/name - [image]/name_regex
#img_name_regex = ^.*(cirros|testvm).*$
# Role required for users to be able to create Swift containers
# (string value)
# Deprecated group/name - [role]/swift_operator_role
#swift_operator_role = Member
# User role that has reseller admin (string value)
# Deprecated group/name - [role]/swift_reseller_admin_role
#swift_reseller_admin_role = ResellerAdmin
# Role required for users to be able to manage Heat stacks (string
# value)
# Deprecated group/name - [role]/heat_stack_owner_role
#heat_stack_owner_role = heat_stack_owner
# Role for Heat template-defined users (string value)
# Deprecated group/name - [role]/heat_stack_user_role
#heat_stack_user_role = heat_stack_user
# Primary flavor RAM size used by most of the test cases (integer
# value)
#flavor_ref_ram = 64
# Alternate reference flavor RAM size used by test thatneed two
# flavors, like those that resize an instnace (integer value)
#flavor_ref_alt_ram = 128
# RAM size flavor used for orchestration test cases (integer value)
#heat_instance_type_ram = 64
[users_context]

View File

@ -48,9 +48,8 @@ def list_opts():
nova_utils.NOVA_BENCHMARK_OPTS,
sahara_utils.SAHARA_BENCHMARK_OPTS,
vm_utils.VM_BENCHMARK_OPTS)),
("image",
itertools.chain(tempest_conf.IMAGE_OPTS)),
("role", itertools.chain(tempest_conf.ROLE_OPTS)),
("tempest",
itertools.chain(tempest_conf.TEMPEST_OPTS)),
("roles_context", itertools.chain(roles.ROLES_CONTEXT_OPTS)),
("users_context", itertools.chain(users.USER_CONTEXT_OPTS)),
("cleanup", itertools.chain(cleanup_base.CLEANUP_OPTS))

View File

@ -38,49 +38,67 @@ from rally.task import utils as task_utils
LOG = logging.getLogger(__name__)
IMAGE_OPTS = [
cfg.StrOpt("cirros_img_url",
TEMPEST_OPTS = [
cfg.StrOpt("img_url",
deprecated_opts=[cfg.DeprecatedOpt("cirros_img_url",
group="image")],
default="http://download.cirros-cloud.net/"
"0.3.4/cirros-0.3.4-x86_64-disk.img",
help="CirrOS image URL"),
cfg.StrOpt("disk_format",
help="image URL"),
cfg.StrOpt("img_disk_format",
deprecated_opts=[cfg.DeprecatedOpt("disk_format",
group="image")],
default="qcow2",
help="Image disk format to use when creating the image"),
cfg.StrOpt("container_format",
cfg.StrOpt("img_container_format",
deprecated_opts=[cfg.DeprecatedOpt("container_format",
group="image")],
default="bare",
help="Image container format to use when creating the image"),
cfg.StrOpt("name_regex",
cfg.StrOpt("img_name_regex",
deprecated_opts=[cfg.DeprecatedOpt("name_regex",
group="image")],
default="^.*(cirros|testvm).*$",
help="Regular expression for name of a public 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. "
"('img_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 = [
"options are not specified in the Tempest config file"),
cfg.StrOpt("swift_operator_role",
deprecated_group="role",
default="Member",
help="Role required for users "
"to be able to create Swift containers"),
cfg.StrOpt("swift_reseller_admin_role",
deprecated_group="role",
default="ResellerAdmin",
help="User role that has reseller admin"),
cfg.StrOpt("heat_stack_owner_role",
deprecated_group="role",
default="heat_stack_owner",
help="Role required for users "
"to be able to manage Heat stacks"),
cfg.StrOpt("heat_stack_user_role",
deprecated_group="role",
default="heat_stack_user",
help="Role for Heat template-defined users")
help="Role for Heat template-defined users"),
cfg.IntOpt("flavor_ref_ram",
default="64",
help="Primary flavor RAM size used by most of the test cases"),
cfg.IntOpt("flavor_ref_alt_ram",
default="128",
help="Alternate reference flavor RAM size used by test that"
"need two flavors, like those that resize an instnace"),
cfg.IntOpt("heat_instance_type_ram",
default="64",
help="RAM size flavor used for orchestration test cases")
]
CONF = cfg.CONF
CONF.register_opts(IMAGE_OPTS, "image")
CONF.register_opts(ROLE_OPTS, "role")
CONF.register_opts(TEMPEST_OPTS, "tempest")
CONF.import_opt("glance_image_delete_timeout",
"rally.plugins.openstack.scenarios.glance.utils",
"benchmark")
@ -120,18 +138,18 @@ class TempestConfig(utils.RandomNameGeneratorMixin):
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()
CONF.tempest.img_url).path.split("/")[-1]
self._download_image()
def _download_cirros_image(self):
def _download_image(self):
img_path = os.path.join(self.data_dir, self.image_name)
if os.path.isfile(img_path):
return
try:
response = requests.get(CONF.image.cirros_img_url, stream=True)
response = requests.get(CONF.tempest.img_url, stream=True)
except requests.ConnectionError as err:
msg = _("Failed to download CirrOS image. "
msg = _("Failed to download image. "
"Possibly there is no connection to Internet. "
"Error: %s.") % (str(err) or "unknown")
raise exceptions.TempestConfigCreationFailure(msg)
@ -145,10 +163,10 @@ class TempestConfig(utils.RandomNameGeneratorMixin):
os.rename(img_path + ".tmp", img_path)
else:
if response.status_code == 404:
msg = _("Failed to download CirrOS image. "
msg = _("Failed to download image. "
"Image was not found.")
else:
msg = _("Failed to download CirrOS image. "
msg = _("Failed to download image. "
"HTTP error code %d.") % response.status_code
raise exceptions.TempestConfigCreationFailure(msg)
@ -273,9 +291,9 @@ class TempestConfig(utils.RandomNameGeneratorMixin):
def _configure_object_storage(self, section_name="object-storage"):
self.conf.set(section_name, "operator_role",
CONF.role.swift_operator_role)
CONF.tempest.swift_operator_role)
self.conf.set(section_name, "reseller_admin_role",
CONF.role.swift_reseller_admin_role)
CONF.tempest.swift_reseller_admin_role)
def _configure_scenario(self, section_name="scenario"):
self.conf.set(section_name, "img_dir", self.data_dir)
@ -314,9 +332,9 @@ class TempestConfig(utils.RandomNameGeneratorMixin):
def _configure_orchestration(self, section_name="orchestration"):
self.conf.set(section_name, "stack_owner_role",
CONF.role.heat_stack_owner_role)
CONF.tempest.heat_stack_owner_role)
self.conf.set(section_name, "stack_user_role",
CONF.role.heat_stack_user_role)
CONF.tempest.heat_stack_user_role)
def generate(self, conf_path=None):
for name, method in inspect.getmembers(self, inspect.ismethod):
@ -344,7 +362,7 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin):
self.conf.read(conf_path)
self.image_name = parse.urlparse(
CONF.image.cirros_img_url).path.split("/")[-1]
CONF.tempest.img_url).path.split("/")[-1]
self._created_roles = []
self._created_images = []
@ -358,9 +376,11 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin):
self._configure_option("compute", "image_ref_alt",
self._discover_or_create_image)
self._configure_option("compute", "flavor_ref",
self._discover_or_create_flavor, 64)
self._discover_or_create_flavor,
CONF.tempest.flavor_ref_ram)
self._configure_option("compute", "flavor_ref_alt",
self._discover_or_create_flavor, 128)
self._discover_or_create_flavor,
CONF.tempest.flavor_ref_alt_ram)
if "neutron" in self.available_services:
neutronclient = self.clients.neutron()
if neutronclient.list_networks(shared=True)["networks"]:
@ -377,7 +397,8 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin):
self._create_network_resources)
if "heat" in self.available_services:
self._configure_option("orchestration", "instance_type",
self._discover_or_create_flavor, 64)
self._discover_or_create_flavor,
CONF.tempest.heat_instance_type_ram)
_write_config(self.conf_path, self.conf)
@ -397,10 +418,10 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin):
def _create_tempest_roles(self):
keystoneclient = self.clients.verified_keystone()
roles = [CONF.role.swift_operator_role,
CONF.role.swift_reseller_admin_role,
CONF.role.heat_stack_owner_role,
CONF.role.heat_stack_user_role]
roles = [CONF.tempest.swift_operator_role,
CONF.tempest.swift_reseller_admin_role,
CONF.tempest.heat_stack_owner_role,
CONF.tempest.heat_stack_user_role]
existing_roles = set(role.name for role in keystoneclient.roles.list())
for role in roles:
@ -428,14 +449,14 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin):
def _discover_or_create_image(self):
glance_wrapper = glance.wrap(self.clients.glance, self)
if CONF.image.name_regex:
if CONF.tempest.img_name_regex:
LOG.debug("Trying to discover a public image with name matching "
"regular expression '%s'. Note that case insensitive "
"matching is performed" % CONF.image.name_regex)
"matching is performed" % CONF.tempest.img_name_regex)
images = glance_wrapper.list_images(status="active",
visibility="public")
for img in images:
if img.name and re.match(CONF.image.name_regex,
if img.name and re.match(CONF.tempest.img_name_regex,
img.name, re.IGNORECASE):
LOG.debug(
"The following public image discovered: '{0}'. "
@ -443,12 +464,12 @@ class TempestResourcesContext(utils.RandomNameGeneratorMixin):
return img
LOG.debug("There is no public image with name matching "
"regular expression '%s'" % CONF.image.name_regex)
"regular expression '%s'" % CONF.tempest.img_name_regex)
params = {
"name": self.generate_random_name(),
"disk_format": CONF.image.disk_format,
"container_format": CONF.image.container_format,
"disk_format": CONF.tempest.img_disk_format,
"container_format": CONF.tempest.img_container_format,
"image_location": os.path.join(_create_or_get_data_dir(),
self.image_name),
"visibility": "public"

View File

@ -61,26 +61,26 @@ class TempestConfigTestCase(test.TestCase):
@mock.patch("os.rename")
@mock.patch("six.moves.builtins.open", side_effect=mock.mock_open())
@mock.patch("requests.get", return_value=mock.MagicMock(status_code=200))
def test__download_cirros_image_success(self, mock_get,
mock_open, mock_rename):
def test__download_image_success(self, mock_get,
mock_open, mock_rename):
self.mock_isfile.return_value = False
self.tempest_conf._download_cirros_image()
self.tempest_conf._download_image()
mock_get.assert_called_once_with(
CONF.image.cirros_img_url, stream=True)
CONF.tempest.img_url, stream=True)
@mock.patch("requests.get")
@ddt.data(404, 500)
def test__download_cirros_image_failure(self, status_code, mock_get):
def test__download_image_failure(self, status_code, mock_get):
self.mock_isfile.return_value = False
mock_get.return_value = mock.MagicMock(status_code=status_code)
self.assertRaises(exceptions.TempestConfigCreationFailure,
self.tempest_conf._download_cirros_image)
self.tempest_conf._download_image)
@mock.patch("requests.get", side_effect=requests.ConnectionError())
def test__download_cirros_image_connection_error(self, mock_requests_get):
def test__download_image_connection_error(self, mock_requests_get):
self.mock_isfile.return_value = False
self.assertRaises(exceptions.TempestConfigCreationFailure,
self.tempest_conf._download_cirros_image)
self.tempest_conf._download_image)
@ddt.data({"publicURL": "test_url"},
{"interface": "public", "url": "test_url"})
@ -228,8 +228,8 @@ class TempestConfigTestCase(test.TestCase):
self.tempest_conf._configure_object_storage()
expected = (
("operator_role", CONF.role.swift_operator_role),
("reseller_admin_role", CONF.role.swift_reseller_admin_role))
("operator_role", CONF.tempest.swift_operator_role),
("reseller_admin_role", CONF.tempest.swift_reseller_admin_role))
result = self.tempest_conf.conf.items("object-storage")
for item in expected:
self.assertIn(item, result)
@ -238,8 +238,8 @@ class TempestConfigTestCase(test.TestCase):
self.tempest_conf._configure_orchestration()
expected = (
("stack_owner_role", CONF.role.heat_stack_owner_role),
("stack_user_role", CONF.role.heat_stack_user_role))
("stack_owner_role", CONF.tempest.heat_stack_owner_role),
("stack_user_role", CONF.tempest.heat_stack_user_role))
result = self.tempest_conf.conf.items("orchestration")
for item in expected:
self.assertIn(item, result)
@ -248,7 +248,7 @@ class TempestConfigTestCase(test.TestCase):
self.tempest_conf._configure_scenario()
image_name = parse.urlparse(
config.CONF.image.cirros_img_url).path.split("/")[-1]
config.CONF.tempest.img_url).path.split("/")[-1]
expected = (("img_dir", self.tempest_conf.data_dir),
("img_file", image_name))
result = self.tempest_conf.conf.items("scenario")
@ -370,10 +370,10 @@ class TempestResourcesContextTestCase(test.TestCase):
self.assertEqual(mock_neutron_wrapper_create_network.call_count, 0)
def test__create_tempest_roles(self):
role1 = CONF.role.swift_operator_role
role2 = CONF.role.swift_reseller_admin_role
role3 = CONF.role.heat_stack_owner_role
role4 = CONF.role.heat_stack_user_role
role1 = CONF.tempest.swift_operator_role
role2 = CONF.tempest.swift_reseller_admin_role
role3 = CONF.tempest.heat_stack_owner_role
role4 = CONF.tempest.heat_stack_user_role
client = self.context.clients.verified_keystone()
client.roles.list.return_value = [fakes.FakeRole(name=role1),
@ -423,9 +423,9 @@ class TempestResourcesContextTestCase(test.TestCase):
mock_wrap.assert_called_once_with(self.context.clients.glance,
self.context)
client.create_image.assert_called_once_with(
container_format=CONF.image.container_format,
container_format=CONF.tempest.img_container_format,
image_location=mock.ANY,
disk_format=CONF.image.disk_format,
disk_format=CONF.tempest.img_disk_format,
name=mock.ANY,
visibility="public")