diff --git a/etc/rally/rally.conf.sample b/etc/rally/rally.conf.sample
index 95cd83dfe7..676911e9e4 100644
--- a/etc/rally/rally.conf.sample
+++ b/etc/rally/rally.conf.sample
@@ -615,14 +615,29 @@
# From rally
#
-# Version of cirros image (string value)
-#cirros_version = 0.3.4
+# 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 image name (string value)
-#cirros_image = cirros-0.3.4-x86_64-disk.img
-# Cirros image base URL (string value)
-#cirros_base_url = http://download.cirros-cloud.net
+[role]
+
+#
+# From rally
+#
+
+# Role required for users to be able to create Swift containers
+# (string value)
+#swift_operator_role = Member
+
+# User role that has reseller admin (string value)
+#swift_reseller_admin_role = ResellerAdmin
+
+# Role required for users to be able to manage Heat stacks (string
+# value)
+#heat_stack_owner_role = heat_stack_owner
+
+# Role for Heat template-defined users (string value)
+#heat_stack_user_role = heat_stack_user
[users_context]
diff --git a/rally/common/opts.py b/rally/common/opts.py
index dfd5a2bbab..fc5ad25f1f 100644
--- a/rally/common/opts.py
+++ b/rally/common/opts.py
@@ -43,6 +43,7 @@ def list_opts():
ec2_utils.EC2_BENCHMARK_OPTS)),
("image",
itertools.chain(tempest_conf.IMAGE_OPTS)),
+ ("role", itertools.chain(tempest_conf.ROLE_OPTS)),
("users_context", itertools.chain(users.USER_CONTEXT_OPTS)),
("cleanup", itertools.chain(cleanup_base.CLEANUP_OPTS))
]
diff --git a/rally/exceptions.py b/rally/exceptions.py
index 9f9a33ec32..04f0c3be19 100644
--- a/rally/exceptions.py
+++ b/rally/exceptions.py
@@ -231,3 +231,11 @@ class InvalidHostException(RallyException):
class MultipleMatchesFound(RallyException):
msg_fmt = _("Found multiple %(needle)s: %(haystack)s")
+
+
+class TempestConfigCreationFailure(RallyException):
+ msg_fmt = _("Unable to create Tempest config file: %(message)s")
+
+
+class TempestResourceCreationFailure(RallyException):
+ msg_fmt = _("Unable to create resource needed for Tempest: %(message)s")
diff --git a/rally/plugins/openstack/context/not_for_production/tempest.py b/rally/plugins/openstack/context/not_for_production/tempest.py
index 09fc373df0..83100e6690 100644
--- a/rally/plugins/openstack/context/not_for_production/tempest.py
+++ b/rally/plugins/openstack/context/not_for_production/tempest.py
@@ -23,7 +23,6 @@ from rally.common import utils
from rally import consts
from rally import exceptions
from rally.task import context
-from rally.verification.tempest import config
from rally.verification.tempest import tempest
LOG = logging.getLogger(__name__)
@@ -65,7 +64,7 @@ class Tempest(context.Context):
msg = _("Failing to install tempest.")
LOG.error(msg)
raise exceptions.BenchmarkSetupFailure(msg)
- except config.TempestConfigCreationFailure:
+ except exceptions.TempestConfigCreationFailure:
msg = _("Failing to configure tempest.")
LOG.error(msg)
raise exceptions.BenchmarkSetupFailure(msg)
diff --git a/rally/verification/tempest/config.ini b/rally/verification/tempest/config.ini
index f6eaf3186e..448294fdb8 100644
--- a/rally/verification/tempest/config.ini
+++ b/rally/verification/tempest/config.ini
@@ -3,50 +3,55 @@ debug = True
log_file = tempest.log
use_stderr = False
-[auth]
-allow_tenant_isolation = False
-
[boto]
-instance_type = m1.nano
http_socket_timeout = 30
-build_interval = 1
-build_timeout = 196
[compute]
-ssh_connect_method = floating
+image_ref =
+image_ref_alt =
+flavor_ref =
+flavor_ref_alt =
+ssh_user = cirros
image_ssh_user = cirros
image_alt_ssh_user = cirros
-network_for_ssh = private
-ssh_user = cirros
-build_interval = 1
-build_timeout = 196
[compute-feature-enabled]
-change_password = False
live_migration = False
-block_migration_for_live_migration = False
+resize = True
+vnc_console = False
+attach_encrypted_volume = False
+
+[dashboard]
+
+[data_processing-feature-enabled]
+plugins = vanilla,cdh,mapr,spark,ambari
[identity]
+[image-feature-enabled]
+deactivate_image = True
+
[network]
-tenant_networks_reachable = False
[network-feature-enabled]
-api_extensions = all
+ipv6_subnet_attributes = True
ipv6 = True
+[object-storage]
+
[oslo_concurrency]
+[orchestration]
+max_template_size = 5440000
+max_resources_per_stack = 20000
+
[scenario]
-large_ops_number = 0
-ssh_user = cirros
+large_ops_number = 2
[service_available]
[validation]
-ssh_timeout = 196
-ip_version_for_ssh = 4
+run_validation = False
-[volume]
-build_interval = 1
-build_timeout = 196
+[volume-feature-enabled]
+bootable = True
diff --git a/rally/verification/tempest/config.py b/rally/verification/tempest/config.py
index d1f9caade3..7af4afbd2a 100644
--- a/rally/verification/tempest/config.py
+++ b/rally/verification/tempest/config.py
@@ -13,10 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import datetime
import inspect
import os
-import time
+import uuid
from oslo_config import cfg
import requests
@@ -30,236 +29,328 @@ from rally.common import objects
from rally import exceptions
from rally import osclients
-
LOG = logging.getLogger(__name__)
-
IMAGE_OPTS = [
- cfg.StrOpt("cirros_version",
- default="0.3.4",
- help="Version of cirros image"),
- cfg.StrOpt("cirros_image",
- default="cirros-0.3.4-x86_64-disk.img",
- help="Cirros image name"),
- cfg.StrOpt("cirros_base_url",
- default="http://download.cirros-cloud.net",
- help="Cirros image base URL"),
+ cfg.StrOpt("cirros_img_url",
+ default="http://download.cirros-cloud.net/"
+ "0.3.4/cirros-0.3.4-x86_64-disk.img",
+ help="CirrOS image URL")
]
+
+ROLE_OPTS = [
+ cfg.StrOpt("swift_operator_role",
+ default="Member",
+ help="Role required for users "
+ "to be able to create Swift containers"),
+ cfg.StrOpt("swift_reseller_admin_role",
+ default="ResellerAdmin",
+ help="User role that has reseller admin"),
+ cfg.StrOpt("heat_stack_owner_role",
+ default="heat_stack_owner",
+ help="Role required for users "
+ "to be able to manage Heat stacks"),
+ cfg.StrOpt("heat_stack_user_role",
+ default="heat_stack_user",
+ help="Role for Heat template-defined users")
+]
+
CONF = cfg.CONF
CONF.register_opts(IMAGE_OPTS, "image")
+CONF.register_opts(ROLE_OPTS, "role")
+
+IMAGE_NAME = parse.urlparse(CONF.image.cirros_img_url).path.split("/")[-1]
-class TempestConfigCreationFailure(exceptions.RallyException):
- msg_fmt = _("Unable create tempest.conf: '%(message)s'")
+def _create_or_get_data_dir():
+ data_dir = os.path.join(
+ os.path.expanduser("~"), ".rally", "tempest", "data")
+ if not os.path.exists(data_dir):
+ os.makedirs(data_dir)
+
+ return data_dir
-class TempestConf(object):
+def _write_config(conf_path, conf_data):
+ with open(conf_path, "w+") as conf_file:
+ conf_data.write(conf_file)
+
+
+class TempestConfig(object):
+ """Class to generate Tempest configuration file."""
def __init__(self, deployment):
+ self.deployment = deployment
+
self.endpoint = db.deployment_get(deployment)["admin"]
self.clients = osclients.Clients(objects.Endpoint(**self.endpoint))
- try:
- self.keystoneclient = self.clients.verified_keystone()
- except exceptions.InvalidAdminException:
- msg = (_("Admin permission is required to generate tempest "
- "configuration file. User %s doesn't have admin role.") %
- self.endpoint["username"])
- raise TempestConfigCreationFailure(msg)
+ self.keystone = self.clients.verified_keystone()
self.available_services = self.clients.services().values()
+ self.data_dir = _create_or_get_data_dir()
+
self.conf = configparser.ConfigParser()
self.conf.read(os.path.join(os.path.dirname(__file__), "config.ini"))
- self.deployment = deployment
- self.data_path = os.path.join(os.path.expanduser("~"), ".rally",
- "tempest", "data")
- if not os.path.exists(self.data_path):
- os.makedirs(self.data_path)
- self.img_path = os.path.join(self.data_path,
- CONF.image.cirros_image)
- if not os.path.isfile(self.img_path):
- self._load_img()
- def _load_img(self):
- cirros_url = ("%s/%s/%s" %
- (CONF.image.cirros_base_url,
- CONF.image.cirros_version,
- CONF.image.cirros_image))
+ self._download_cirros_image()
+
+ def _download_cirros_image(self):
+ img_path = os.path.join(self.data_dir, IMAGE_NAME)
+ if os.path.isfile(img_path):
+ return
+
try:
- response = requests.get(cirros_url, stream=True)
+ response = requests.get(CONF.image.cirros_img_url, stream=True)
except requests.ConnectionError as err:
- msg = _("Error on downloading cirros image, possibly"
- " no connection to Internet with message %s") % str(err)
- raise TempestConfigCreationFailure(msg)
+ msg = _("Failed to download CirrOS image. "
+ "Possibly there is no connection to Internet. "
+ "Error: %s.") % (str(err) or "unknown")
+ raise exceptions.TempestConfigCreationFailure(msg)
+
if response.status_code == 200:
- with open(self.img_path + ".tmp", "wb") as img_file:
+ with open(img_path + ".tmp", "wb") as img_file:
for chunk in response.iter_content(chunk_size=1024):
if chunk: # filter out keep-alive new chunks
img_file.write(chunk)
img_file.flush()
- os.rename(self.img_path + ".tmp", self.img_path)
+ os.rename(img_path + ".tmp", img_path)
else:
if response.status_code == 404:
- msg = _("Error on downloading cirros image, possibly"
- "invalid cirros_version or cirros_image in rally.conf")
+ msg = _("Failed to download CirrOS image. "
+ "Image was not found.")
else:
- msg = _("Error on downloading cirros image, "
- "HTTP error code %s") % response.getcode()
- raise TempestConfigCreationFailure(msg)
+ msg = _("Failed to download CirrOS image. "
+ "HTTP error code %d.") % response.status_code
+ raise exceptions.TempestConfigCreationFailure(msg)
- def _get_url(self, servicename):
- services_type2name_map = self.clients.services()
- for service in self.keystoneclient.auth_ref["serviceCatalog"]:
- if services_type2name_map.get(service["type"]) == servicename:
+ def _get_service_url(self, service_type):
+ for service in self.keystone.auth_ref["serviceCatalog"]:
+ if self.clients.services().get(service["type"]) == service_type:
return service["endpoints"][0]["publicURL"]
- def _set_default(self):
- # Nothing to set up in this section for now
- pass
-
- def _set_oslo_concurrency(self, section_name="oslo_concurrency"):
- lock_path = os.path.join(self.data_path,
- "lock_files_%s" % self.deployment)
- if not os.path.exists(lock_path):
- os.makedirs(lock_path)
- self.conf.set(section_name, "lock_path", lock_path)
-
- def _set_boto(self, section_name="boto"):
- self.conf.set(section_name, "ec2_url", self._get_url("ec2"))
- self.conf.set(section_name, "s3_url", self._get_url("s3"))
- materials_path = os.path.join(self.data_path, "s3materials")
- self.conf.set(section_name, "s3_materials_path", materials_path)
+ def _configure_boto(self, section_name="boto"):
+ self.conf.set(section_name, "ec2_url", self._get_service_url("ec2"))
+ self.conf.set(section_name, "s3_url", self._get_service_url("s3"))
+ self.conf.set(section_name, "s3_materials_path",
+ os.path.join(self.data_dir, "s3materials"))
# TODO(olkonami): find out how can we get ami, ari, aki manifest files
- def _set_compute_images(self, section_name="compute"):
- glanceclient = self.clients.glance()
- image_list = [img for img in glanceclient.images.list()
- if img.status.lower() == "active" and
- img.name is not None and "cirros" in img.name]
- # Upload new images if there are no
- # necessary images in the cloud (cirros)
- while len(image_list) < 2:
- now = (datetime.datetime.fromtimestamp(time.time()).
- strftime("%Y_%m_%d_%H_%M_%S"))
- try:
- image = glanceclient.images.create(name=("cirros_%s" % now),
- disk_format="qcow2",
- container_format="bare")
- image.update(data=open(self.img_path, "rb"))
- image_list.append(image)
- except Exception as e:
- msg = _("There are no desired images (cirros) or only one and "
- "new image could not be created.\n"
- "Reason: %s") % getattr(e, "message", "unknown")
- raise TempestConfigCreationFailure(msg)
- self.conf.set(section_name, "image_ref", image_list[0].id)
- self.conf.set(section_name, "image_ref_alt", image_list[1].id)
+ def _configure_default(self, section_name="DEFAULT"):
+ # Nothing to configure in this section for now
+ pass
- def _set_compute_flavors(self, section_name="compute"):
- novaclient = self.clients.nova()
- flavor_list = sorted(novaclient.flavors.list(),
- key=lambda flv: flv.ram)
- # Create new flavors if they are missing
- while len(flavor_list) < 2:
- now = (datetime.datetime.fromtimestamp(time.time()).
- strftime("%Y_%m_%d_%H_%M_%S"))
- try:
- flv = novaclient.flavors.create("m1.tiny_%s" % now, 512, 1, 1)
- flavor_list.append(flv)
- except Exception as e:
- msg = _("There are no desired flavors or only one and "
- "new flavor could not be created.\n"
- "Reason: %s") % getattr(e, "message", "unknown")
- raise TempestConfigCreationFailure(msg)
- self.conf.set(section_name, "flavor_ref", flavor_list[0].id)
- self.conf.set(section_name, "flavor_ref_alt", flavor_list[1].id)
+ def _configure_dashboard(self, section_name="dashboard"):
+ url = "http://%s/" % parse.urlparse(self.endpoint["auth_url"]).hostname
+ self.conf.set(section_name, "dashboard_url", url)
- def _set_compute_ssh_connect_method(self, section_name="compute"):
- if "neutron" in self.available_services:
- self.conf.set(section_name, "ssh_connect_method", "floating")
- else:
- self.conf.set(section_name, "ssh_connect_method", "fixed")
-
- def _set_identity(self, section_name="identity"):
+ def _configure_identity(self, section_name="identity"):
self.conf.set(section_name, "username", self.endpoint["username"])
self.conf.set(section_name, "password", self.endpoint["password"])
self.conf.set(section_name, "tenant_name",
self.endpoint["tenant_name"])
- self.conf.set(section_name, "alt_username", self.endpoint["username"])
- self.conf.set(section_name, "alt_password", self.endpoint["password"])
- self.conf.set(section_name, "alt_tenant_name",
- self.endpoint["tenant_name"])
+
self.conf.set(section_name, "admin_username",
self.endpoint["username"])
self.conf.set(section_name, "admin_password",
self.endpoint["password"])
self.conf.set(section_name, "admin_tenant_name",
self.endpoint["tenant_name"])
+
self.conf.set(section_name, "uri", self.endpoint["auth_url"])
+ v2_url_trailer = parse.urlparse(self.endpoint["auth_url"]).path
self.conf.set(section_name, "uri_v3",
- self.endpoint["auth_url"].replace("/v2.0", "/v3"))
+ self.endpoint["auth_url"].replace(v2_url_trailer, "/v3"))
+
self.conf.set(section_name, "admin_domain_name",
self.endpoint["admin_domain_name"])
- def _set_network(self, section_name="network"):
- if "neutron" in self.available_services:
- neutron = self.clients.neutron()
- public_net = [net for net in neutron.list_networks()["networks"] if
- net["status"] == "ACTIVE" and
- net["router:external"] is True]
- if public_net:
- net_id = public_net[0]["id"]
- self.conf.set(section_name, "public_network_id", net_id)
- public_router = neutron.list_routers(
- network_id=net_id)["routers"][0]
- self.conf.set(section_name, "public_router_id",
- public_router["id"])
- subnets = neutron.list_subnets(network_id=net_id)["subnets"]
- if subnets:
- subnet = subnets[0]
- else:
- # TODO(akurilin): create public subnet
- LOG.warn("No public subnet is found.")
- else:
- subnets = neutron.list_subnets()["subnets"]
- if subnets:
- subnet = subnets[0]
- else:
- # TODO(akurilin): create subnet
- LOG.warn("No subnet is found.")
- self.conf.set(section_name, "tenant_network_cidr", subnet["cidr"])
- else:
- network = self.clients.nova().networks.list()[0]
- self.conf.set(section_name, "tenant_network_cidr", network.cidr)
+ # The compute section is configured in context class for Tempest resources.
+ # Options which are configured there: 'image_ref', 'image_ref_alt',
+ # 'flavor_ref', 'flavor_ref_alt'.
- def _set_service_available(self, section_name="service_available"):
- services = ["neutron", "heat", "ceilometer", "swift",
- "cinder", "nova", "glance"]
+ def _configure_network(self, section_name="network"):
+ if "neutron" in self.available_services:
+ neutronclient = self.clients.neutron()
+ public_nets = [net for net
+ in neutronclient.list_networks()["networks"]
+ if net["status"] == "ACTIVE" and
+ net["router:external"] is True]
+ if public_nets:
+ net_id = public_nets[0]["id"]
+ self.conf.set(section_name, "public_network_id", net_id)
+ else:
+ novaclient = self.clients.nova()
+ net_name = next(net.human_id for net in novaclient.networks.list()
+ if net.human_id is not None)
+ self.conf.set("compute", "fixed_network_name", net_name)
+ self.conf.set("compute", "network_for_ssh", net_name)
+
+ def _configure_network_feature_enabled(
+ self, section_name="network-feature-enabled"):
+ if "neutron" in self.available_services:
+ neutronclient = self.clients.neutron()
+ ext_list = [ext["alias"] for ext in
+ neutronclient.list_ext("/extensions")["extensions"]]
+ ext_list_str = ",".join(ext_list)
+ self.conf.set(section_name, "api_extensions", ext_list_str)
+
+ def _configure_oslo_concurrency(self, section_name="oslo_concurrency"):
+ lock_path = os.path.join(self.data_dir,
+ "lock_files_%s" % self.deployment)
+ if not os.path.exists(lock_path):
+ os.makedirs(lock_path)
+ self.conf.set(section_name, "lock_path", lock_path)
+
+ def _configure_object_storage(self, section_name="object-storage"):
+ self.conf.set(section_name, "operator_role",
+ CONF.role.swift_operator_role)
+ self.conf.set(section_name, "reseller_admin_role",
+ CONF.role.swift_reseller_admin_role)
+
+ def _configure_scenario(self, section_name="scenario"):
+ self.conf.set(section_name, "img_dir", self.data_dir)
+ self.conf.set(section_name, "img_file", IMAGE_NAME)
+
+ def _configure_service_available(self, section_name="service_available"):
+ services = ["ceilometer", "cinder", "glance",
+ "heat", "neutron", "nova", "sahara", "swift"]
for service in services:
+ # Convert boolean to string because ConfigParser fails
+ # on attempt to get option with boolean value
self.conf.set(section_name, service,
str(service in self.available_services))
horizon_url = ("http://" +
parse.urlparse(self.endpoint["auth_url"]).hostname)
try:
horizon_req = requests.get(horizon_url)
- except requests.RequestException as e:
- LOG.debug("Failed to connect to Horizon: %s" % e)
+ except requests.RequestException:
+ LOG.debug("Horizon is unavailable!")
horizon_availability = False
else:
horizon_availability = (horizon_req.status_code == 200)
- # convert boolean to string because ConfigParser fails
+ # Convert boolean to string because ConfigParser fails
# on attempt to get option with boolean value
self.conf.set(section_name, "horizon", str(horizon_availability))
- def write_config(self, file_name):
- with open(file_name, "w+") as f:
- self.conf.write(f)
+ def _configure_validation(self, section_name="validation"):
+ if "neutron" in self.available_services:
+ self.conf.set(section_name, "connect_method", "floating")
+ else:
+ self.conf.set(section_name, "connect_method", "fixed")
- def generate(self, file_name=None):
- for name, func in inspect.getmembers(self, predicate=inspect.ismethod):
- if name.startswith("_set_"):
- func()
- if file_name:
- self.write_config(file_name)
+ def generate(self, conf_path=None):
+ for name, method in inspect.getmembers(self, inspect.ismethod):
+ if name.startswith("_configure_"):
+ method()
- return self.conf
+ if conf_path:
+ _write_config(conf_path, self.conf)
+
+
+class TempestResourcesContext(object):
+ """Context class to create/delete resources needed for Tempest."""
+
+ def __init__(self, deployment, conf_path):
+ endpoint = db.deployment_get(deployment)["admin"]
+ self.clients = osclients.Clients(objects.Endpoint(**endpoint))
+ self.keystone = self.clients.verified_keystone()
+
+ self.conf_path = conf_path
+ self.conf = configparser.ConfigParser()
+ self.conf.read(conf_path)
+
+ def __enter__(self):
+ self._created_roles = []
+ self._created_images = []
+ self._created_flavors = []
+
+ self._create_tempest_roles()
+ self._configure_option("image_ref", self._create_image)
+ self._configure_option("image_ref_alt", self._create_image)
+ self._configure_option("flavor_ref", self._create_flavor, 64)
+ self._configure_option("flavor_ref_alt", self._create_flavor, 128)
+
+ _write_config(self.conf_path, self.conf)
+
+ def __exit__(self, exc_type, exc_value, exc_traceback):
+ self._cleanup_roles()
+ self._cleanup_resource("image", self._created_images)
+ self._cleanup_resource("flavor", self._created_flavors)
+
+ def _create_tempest_roles(self):
+ roles = [CONF.role.swift_operator_role,
+ CONF.role.swift_reseller_admin_role,
+ CONF.role.heat_stack_owner_role,
+ CONF.role.heat_stack_user_role]
+ existing_roles = set(role.name for role in self.keystone.roles.list())
+
+ for role in roles:
+ if role not in existing_roles:
+ LOG.debug("Creating role '%s'" % role)
+ self._created_roles.append(self.keystone.roles.create(role))
+
+ def _configure_option(self, option, create_method, *args, **kwargs):
+ option_value = self.conf.get("compute", option)
+ if not option_value:
+ LOG.debug("Option '%s' is not configured" % option)
+ resource = create_method(*args, **kwargs)
+ self.conf.set("compute", option, resource.id)
+ LOG.debug("Option '{opt}' is configured. {opt} = {resource_id}"
+ .format(opt=option, resource_id=resource.id))
+ else:
+ LOG.debug("Option '{opt}' was configured manually "
+ "in Tempest config file. {opt} = {opt_val}"
+ .format(opt=option, opt_val=option_value))
+
+ def _create_image(self):
+ glanceclient = self.clients.glance()
+ image_name = "rally-verify-cirros-img-%s" % uuid.uuid4()
+ LOG.debug("Creating image '%s'" % image_name)
+ try:
+ image = glanceclient.images.create(
+ name=image_name,
+ disk_format="qcow2",
+ container_format="bare",
+ is_public=True)
+ self._created_images.append(image)
+ image.update(data=open(
+ os.path.join(_create_or_get_data_dir(), IMAGE_NAME), "rb"))
+ except Exception as exc:
+ msg = _("Image could not be created. "
+ "Reason: %s") % (str(exc) or "unknown")
+ raise exceptions.TempestResourceCreationFailure(msg)
+
+ return image
+
+ def _create_flavor(self, flv_ram):
+ novaclient = self.clients.nova()
+ flavor_name = "m1.rally-verify-flv-%s" % uuid.uuid4()
+ LOG.debug("Creating flavor '%s'" % flavor_name)
+ try:
+ flavor = novaclient.flavors.create(
+ flavor_name, ram=flv_ram, vcpus=1, disk=0)
+ except Exception as exc:
+ msg = _("Flavor could not be created. "
+ "Reason: %s") % (str(exc) or "unknown")
+ raise exceptions.TempestResourceCreationFailure(msg)
+
+ self._created_flavors.append(flavor)
+
+ return flavor
+
+ def _cleanup_roles(self):
+ for role in self._created_roles:
+ LOG.debug("Deleting role '%s'" % role.name)
+ role.delete()
+
+ def _cleanup_resource(self, resource_type, created_resources):
+ for res in created_resources:
+ LOG.debug("Deleting %s '%s'" % (resource_type, res.name))
+ if res.id == self.conf.get("compute", "%s_ref" % resource_type):
+ self.conf.set("compute", "%s_ref" % resource_type, "")
+ else:
+ self.conf.set("compute", "%s_ref_alt" % resource_type, "")
+ res.delete()
+
+ _write_config(self.conf_path, self.conf)
diff --git a/rally/verification/tempest/tempest.py b/rally/verification/tempest/tempest.py
index 5919ad047b..a9adec6e01 100644
--- a/rally/verification/tempest/tempest.py
+++ b/rally/verification/tempest/tempest.py
@@ -229,7 +229,7 @@ class Tempest(object):
msg = _("Creation of configuration file for tempest.")
LOG.info(_("Starting: ") + msg)
- config.TempestConf(self.deployment).generate(self.config_file)
+ config.TempestConfig(self.deployment).generate(self.config_file)
LOG.info(_("Completed: ") + msg)
else:
LOG.info("Tempest is already configured.")
@@ -345,8 +345,12 @@ class Tempest(object):
"log_file": log_file or self.log_file_raw
})
LOG.debug("Test(s) started by the command: %s" % test_cmd)
- subprocess.check_call(test_cmd, cwd=self.path(),
- env=self.env, shell=True)
+ # Create all resources needed for Tempest before running tests.
+ # Once tests finish, all created resources will be deleted.
+ with config.TempestResourcesContext(self.deployment, self.config_file):
+ # Run tests
+ subprocess.check_call(test_cmd, cwd=self.path(),
+ env=self.env, shell=True)
def discover_tests(self, pattern=""):
"""Return a set of discovered tests which match given pattern."""
diff --git a/tests/ci/rally-gate/index_verify.mako b/tests/ci/rally-gate/index_verify.mako
index 453691504d..bb61f0cf1b 100644
--- a/tests/ci/rally-gate/index_verify.mako
+++ b/tests/ci/rally-gate/index_verify.mako
@@ -62,27 +62,26 @@
[${vr_1_html}]
- Display raw results in HTML
+ Display raw results in HTML
$ rally verify results --html
[${vr_1_json}]
- Display raw results in JSON
+ Display raw results in JSON
$ rally verify results --json
[${vs_1}]
- Display results table of the verification
+ Display results table of the verification
$ rally verify show
[${vsd_1}]
- Display results table of the verification with detailed errors
+ Display results table of the verification with detailed errors
$ rally verify show --detailed
or $ rally verify detailed
-
Second verification run
-
@@ -92,22 +91,22 @@
-
[${vr_2_html}]
- Display results in HTML
+ Display results in HTML
$ rally verify results --html
-
[${vr_2_json}]
- Display results in JSON
+ Display results in JSON
$ rally verify results --json
-
[${vs_2}]
- Display table results of the verification
+ Display table results of the verification
$ rally verify show
-
[${vsd_2}]
- Display table results of the verification with detailed errors
+ Display table results of the verification with detailed errors
$ rally verify show --detailed
or $ rally verify detailed
diff --git a/tests/ci/rally-verify.sh b/tests/ci/rally-verify.sh
index 64e1cef53d..e29562f67e 100755
--- a/tests/ci/rally-verify.sh
+++ b/tests/ci/rally-verify.sh
@@ -48,8 +48,8 @@ gzip -9 ${RESULTS_DIR}/tempest_installation.txt
gzip -9 ${RESULTS_DIR}/tempest_config_generation.txt
function do_verification {
- OUTPUT_FILE=${RESULTS_DIR}/${1}_verification_${SET_NAME}_set.txt
- rally --rally-debug verify start --regex tempest.api.compute.servers.test_servers > ${OUTPUT_FILE} 2>&1
+ OUTPUT_FILE=${RESULTS_DIR}/${1}_verification_${2}_set.txt
+ rally --rally-debug verify start --set ${2} > ${OUTPUT_FILE} 2>&1
RESULTS+="v${1}=$(do_status $?) "
gzip -9 ${OUTPUT_FILE}
source ~/.rally/globals && VERIFICATIONS[${1}]=${RALLY_VERIFICATION}
@@ -57,24 +57,24 @@ function do_verification {
# Check different "rally verify" commands, which displays verification results
for OUTPUT_FORMAT in "html" "json"
do
- OUTPUT_FILE=${RESULTS_DIR}/${1}_verify_results.${OUTPUT_FORMAT}
+ OUTPUT_FILE=${RESULTS_DIR}/${1}_verify_results_${2}_set.${OUTPUT_FORMAT}
rally verify results --uuid ${RALLY_VERIFICATION} --${OUTPUT_FORMAT} --output-file ${OUTPUT_FILE}
RESULTS+="vr_${1}_${OUTPUT_FORMAT}=$(do_status $?) "
gzip -9 ${OUTPUT_FILE}
done
- rally verify show --uuid ${RALLY_VERIFICATION} > ${RESULTS_DIR}/${1}_verify_show.txt
+ rally verify show --uuid ${RALLY_VERIFICATION} > ${RESULTS_DIR}/${1}_verify_show_${2}_set.txt
RESULTS+="vs_${1}=$(do_status $?) "
- gzip -9 ${RESULTS_DIR}/${1}_verify_show.txt
+ gzip -9 ${RESULTS_DIR}/${1}_verify_show_${2}_set.txt
- rally verify show --uuid ${RALLY_VERIFICATION} --detailed > ${RESULTS_DIR}/${1}_verify_show_detailed.txt
+ rally verify show --uuid ${RALLY_VERIFICATION} --detailed > ${RESULTS_DIR}/${1}_verify_show_${2}_set_detailed.txt
RESULTS+="vsd_${1}=$(do_status $?) "
- gzip -9 ${RESULTS_DIR}/${1}_verify_show_detailed.txt
+ gzip -9 ${RESULTS_DIR}/${1}_verify_show_${2}_set_detailed.txt
}
function main {
- do_verification 1
- do_verification 2
+ do_verification 1 compute
+ do_verification 2 compute
rally verify list > ${RESULTS_DIR}/verify_list.txt
RESULTS+="l=$(do_status $?) "
diff --git a/tests/unit/plugins/openstack/context/not_for_production/test_tempest.py b/tests/unit/plugins/openstack/context/not_for_production/test_tempest.py
index ca46b549b6..f0f130b536 100644
--- a/tests/unit/plugins/openstack/context/not_for_production/test_tempest.py
+++ b/tests/unit/plugins/openstack/context/not_for_production/test_tempest.py
@@ -17,7 +17,6 @@ import mock
from rally import exceptions
from rally.plugins.openstack.context.not_for_production import tempest
-from rally.verification.tempest import config
from rally.verification.tempest import tempest as tempest_verifier
from tests.unit import test
@@ -75,7 +74,7 @@ class TempestContextTestCase(test.TestCase):
self, mock_tempest_generate_config_file, mock_tempest_is_installed,
mock_tempest_is_configured, mock_mkdir):
mock_tempest_generate_config_file.side_effect = (
- config.TempestConfigCreationFailure()
+ exceptions.TempestConfigCreationFailure()
)
benchmark = tempest.Tempest(self.context)
diff --git a/tests/unit/plugins/openstack/scenarios/tempest/test_tempest.py b/tests/unit/plugins/openstack/scenarios/tempest/test_tempest.py
index 157e983d26..6a30ce7c22 100644
--- a/tests/unit/plugins/openstack/scenarios/tempest/test_tempest.py
+++ b/tests/unit/plugins/openstack/scenarios/tempest/test_tempest.py
@@ -19,7 +19,8 @@ from rally.plugins.openstack.scenarios.tempest import tempest
from rally.verification.tempest import tempest as verifier
from tests.unit import test
-VERIFIER = "rally.verification.tempest.tempest"
+TEMPEST_DIR = "rally.verification.tempest"
+VERIFIER = TEMPEST_DIR + ".tempest"
TS = "rally.plugins.openstack.scenarios.tempest"
@@ -51,7 +52,9 @@ class TempestScenarioTestCase(test.TestCase):
@mock.patch(TS + ".utils.tempfile")
@mock.patch(VERIFIER + ".subprocess")
- def test_single_test(self, mock_subprocess, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_single_test(self, mock_tempest_resources_context,
+ mock_subprocess, mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
fake_test = "tempest.api.fake.test"
@@ -64,7 +67,9 @@ class TempestScenarioTestCase(test.TestCase):
@mock.patch(TS + ".utils.tempfile")
@mock.patch(VERIFIER + ".subprocess")
- def test_single_test_negative(self, mock_subprocess, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_single_test_negative(self, mock_tempest_resources_context,
+ mock_subprocess, mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
fake_test = "tempest.api.network"
@@ -77,7 +82,9 @@ class TempestScenarioTestCase(test.TestCase):
@mock.patch(TS + ".utils.tempfile")
@mock.patch(VERIFIER + ".subprocess")
- def test_single_test_without_prefix(self, mock_subprocess, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_single_test_without_prefix(self, mock_tempest_resources_context,
+ mock_subprocess, mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
self.scenario.single_test("network")
@@ -89,7 +96,9 @@ class TempestScenarioTestCase(test.TestCase):
@mock.patch(TS + ".utils.tempfile")
@mock.patch(VERIFIER + ".subprocess")
- def test_all(self, mock_subprocess, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_all(self, mock_tempest_resources_context,
+ mock_subprocess, mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
self.scenario.all()
@@ -101,7 +110,9 @@ class TempestScenarioTestCase(test.TestCase):
@mock.patch(TS + ".utils.tempfile")
@mock.patch(VERIFIER + ".subprocess")
- def test_set_smoke(self, mock_subprocess, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_set_smoke(self, mock_tempest_resources_context,
+ mock_subprocess, mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
self.scenario.set("smoke")
@@ -113,7 +124,9 @@ class TempestScenarioTestCase(test.TestCase):
@mock.patch(TS + ".utils.tempfile")
@mock.patch(VERIFIER + ".subprocess")
- def test_set_full(self, mock_subprocess, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_set_full(self, mock_tempest_resources_context,
+ mock_subprocess, mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
self.scenario.set("full")
@@ -124,7 +137,9 @@ class TempestScenarioTestCase(test.TestCase):
env=self.verifier.env, shell=True)
@mock.patch(TS + ".utils.tempfile")
- def test_set_from_list(self, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_set_from_list(self, mock_tempest_resources_context,
+ mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
fake_scenarios = ["network", "volume", "baremetal",
@@ -143,7 +158,9 @@ class TempestScenarioTestCase(test.TestCase):
@mock.patch(TS + ".utils.tempfile")
@mock.patch(VERIFIER + ".subprocess")
- def test_set_selective(self, mock_subprocess, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_set_selective(self, mock_tempest_resources_context,
+ mock_subprocess, mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
self.scenario.set("network")
@@ -155,7 +172,9 @@ class TempestScenarioTestCase(test.TestCase):
@mock.patch(TS + ".utils.tempfile")
@mock.patch(VERIFIER + ".subprocess")
- def test_list_of_tests(self, mock_subprocess, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_list_of_tests(self, mock_tempest_resources_context,
+ mock_subprocess, mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
fake_tests = ["tempest.fake.test1", "tempest.fake.test2"]
@@ -168,7 +187,9 @@ class TempestScenarioTestCase(test.TestCase):
@mock.patch(TS + ".utils.tempfile")
@mock.patch(VERIFIER + ".subprocess")
- def test_specific_regex(self, mock_subprocess, mock_tempfile):
+ @mock.patch(TEMPEST_DIR + ".config.TempestResourcesContext")
+ def test_specific_regex(self, mock_tempest_resources_context,
+ mock_subprocess, mock_tempfile):
mock_tempfile.NamedTemporaryFile().name = "/dev/null"
regex = "tempest.fake.test1"
diff --git a/tests/unit/verification/test_config.py b/tests/unit/verification/test_config.py
index 2a649712fb..0e8258f2eb 100644
--- a/tests/unit/verification/test_config.py
+++ b/tests/unit/verification/test_config.py
@@ -19,6 +19,7 @@ import mock
from oslo_config import cfg
import requests
+from rally import exceptions
from rally.verification.tempest import config
from tests.unit import fakes
from tests.unit import test
@@ -37,23 +38,31 @@ class ConfigTestCase(test.TestCase):
def setUp(self, mock_isfile, mock_clients_verified_keystone,
mock_clients_services, mock_deployment_get):
super(ConfigTestCase, self).setUp()
- self.endpoint = {"username": "test",
- "tenant_name": "test",
- "password": "test",
- "auth_url": "http://test/v2.0",
- "permission": "admin",
- "admin_domain_name": "Default"}
+
+ self.endpoint = {
+ "username": "test",
+ "tenant_name": "test",
+ "password": "test",
+ "auth_url": "http://test/v2.0/",
+ "permission": "admin",
+ "admin_domain_name": "Default"
+ }
mock_deployment_get.return_value = {"admin": self.endpoint}
+
self.deployment = "fake_deployment"
- self.conf_generator = config.TempestConf(self.deployment)
+ self.conf_generator = config.TempestConfig(self.deployment)
self.conf_generator.clients.services = mock_clients_services
+ self.context = config.TempestResourcesContext(self.deployment,
+ "/path/to/fake/conf")
+ self.context.conf.add_section("compute")
keystone_patcher = mock.patch("rally.osclients.create_keystone_client")
keystone_patcher.start()
self.addCleanup(keystone_patcher.stop)
- def _remove_default_section(self, items):
- # getting items from configparser by specified section name
+ @staticmethod
+ def _remove_default_section(items):
+ # Getting items from config parser by specified section name
# returns also values from DEFAULT section
defaults = (("log_file", "tempest.log"), ("debug", "True"),
("use_stderr", "False"))
@@ -63,269 +72,223 @@ class ConfigTestCase(test.TestCase):
@mock.patch("rally.verification.tempest.config.os.rename")
@mock.patch("six.moves.builtins.open", side_effect=mock.mock_open(),
create=True)
- def test__load_img_success(self, mock_open, mock_rename, mock_requests):
+ def test__download_cirros_image_success(self, mock_open, mock_rename,
+ mock_requests):
mock_result = mock.MagicMock()
mock_result.status_code = 200
mock_requests.get.return_value = mock_result
- self.conf_generator._load_img()
- cirros_url = ("http://download.cirros-cloud.net/%s/%s" %
- (CONF.image.cirros_version,
- CONF.image.cirros_image))
- mock_requests.get.assert_called_once_with(cirros_url, stream=True)
+ self.conf_generator._download_cirros_image()
+ mock_requests.get.assert_called_once_with(CONF.image.cirros_img_url,
+ stream=True)
@mock.patch("rally.verification.tempest.config.requests")
- def test__load_img_notfound(self, mock_requests):
+ def test__download_cirros_image_notfound(self, mock_requests):
mock_result = mock.MagicMock()
mock_result.status_code = 404
mock_requests.get.return_value = mock_result
- self.assertRaises(config.TempestConfigCreationFailure,
- self.conf_generator._load_img)
+ self.assertRaises(exceptions.TempestConfigCreationFailure,
+ self.conf_generator._download_cirros_image)
- def test__get_url(self):
+ def test__get_service_url(self):
service = "test_service"
service_type = "test_service_type"
url = "test_url"
- # mocked at setUp
- self.conf_generator.keystoneclient.auth_ref = {
- "serviceCatalog": [{
- "name": service,
- "type": service_type,
- "endpoints": [{"publicURL": url}]
- }]}
- self.assertEqual(self.conf_generator._get_url(service), url)
+ # Mocked at setUp
+ self.conf_generator.keystone.auth_ref = {
+ "serviceCatalog": [
+ {
+ "name": service,
+ "type": service_type,
+ "endpoints": [{"publicURL": url}]
+ }
+ ]
+ }
+ self.assertEqual(self.conf_generator._get_service_url(service), url)
- @mock.patch("rally.verification.tempest.config.TempestConf"
- "._get_url")
- def test__set_boto(self, mock_tempest_conf__get_url):
+ @mock.patch("rally.verification.tempest."
+ "config.TempestConfig._get_service_url")
+ def test__configure_boto(self, mock_tempest_config__get_service_url):
url = "test_url"
- mock_tempest_conf__get_url.return_value = url
- self.conf_generator._set_boto()
+ mock_tempest_config__get_service_url.return_value = url
+ s3_materials_path = os.path.join(
+ self.conf_generator.data_dir, "s3materials")
+ self.conf_generator._configure_boto()
expected = (("ec2_url", url),
("s3_url", url),
- ("build_interval", "1"),
- ("build_timeout", "196"),
("http_socket_timeout", "30"),
- ("instance_type", "m1.nano"),
- ("s3_materials_path",
- os.path.join(self.conf_generator.data_path,
- "s3materials")))
+ ("s3_materials_path", s3_materials_path))
results = self._remove_default_section(
self.conf_generator.conf.items("boto"))
self.assertEqual(sorted(expected), sorted(results))
- def test__set_compute_flavors(self):
- mock_novaclient = mock.MagicMock()
- mock_novaclient.flavors.list.return_value = [
- fakes.FakeFlavor(id="id1"), fakes.FakeFlavor(id="id2")]
- mock_nova = mock.MagicMock()
- mock_nova.client.Client.return_value = mock_novaclient
- with mock.patch.dict("sys.modules", {"novaclient": mock_nova}):
- self.conf_generator._set_compute_flavors()
- expected = ("id1", "id2")
- results = (self.conf_generator.conf.get("compute", "flavor_ref"),
- self.conf_generator.conf.get("compute",
- "flavor_ref_alt"))
- self.assertEqual(sorted(expected), sorted(results))
-
- def test__set_compute_flavors_create(self):
- mock_novaclient = mock.MagicMock()
- mock_novaclient.flavors.list.return_value = []
- mock_novaclient.flavors.create.side_effect = [
- fakes.FakeFlavor(id="id1"), fakes.FakeFlavor(id="id2")]
- mock_nova = mock.MagicMock()
- mock_nova.client.Client.return_value = mock_novaclient
- with mock.patch.dict("sys.modules", {"novaclient": mock_nova}):
- self.conf_generator._set_compute_flavors()
- self.assertEqual(mock_novaclient.flavors.create.call_count, 2)
- expected = ("id1", "id2")
- results = (self.conf_generator.conf.get("compute", "flavor_ref"),
- self.conf_generator.conf.get("compute",
- "flavor_ref_alt"))
- self.assertEqual(sorted(expected), sorted(results))
-
- def test__set_compute_flavors_create_fails(self):
- mock_novaclient = mock.MagicMock()
- mock_novaclient.flavors.list.return_value = []
- mock_novaclient.flavors.create.side_effect = Exception()
- mock_nova = mock.MagicMock()
- mock_nova.client.Client.return_value = mock_novaclient
- with mock.patch.dict("sys.modules", {"novaclient": mock_nova}):
- self.assertRaises(config.TempestConfigCreationFailure,
- self.conf_generator._set_compute_flavors)
-
- def test__set_compute_images(self):
- mock_glanceclient = mock.MagicMock()
- mock_glanceclient.images.list.return_value = [
- fakes.FakeImage(id="id1", name="cirros1"),
- fakes.FakeImage(id="id2", name="cirros2")]
- mock_glance = mock.MagicMock()
- mock_glance.Client.return_value = mock_glanceclient
- with mock.patch.dict("sys.modules", {"glanceclient": mock_glance}):
- self.conf_generator._set_compute_images()
- expected = ("id1", "id2")
- results = (self.conf_generator.conf.get("compute", "image_ref"),
- self.conf_generator.conf.get("compute",
- "image_ref_alt"))
- self.assertEqual(sorted(expected), sorted(results))
-
- @mock.patch("six.moves.builtins.open")
- def test__set_compute_images_create(self, mock_open):
- mock_glanceclient = mock.MagicMock()
- mock_glanceclient.images.list.return_value = []
- mock_glanceclient.images.create.side_effect = [
- fakes.FakeImage(id="id1"), fakes.FakeImage(id="id2")]
- mock_glance = mock.MagicMock()
- mock_glance.Client.return_value = mock_glanceclient
- with mock.patch.dict("sys.modules", {"glanceclient": mock_glance}):
- self.conf_generator._set_compute_images()
- self.assertEqual(mock_glanceclient.images.create.call_count, 2)
- expected = ("id1", "id2")
- results = (self.conf_generator.conf.get("compute", "image_ref"),
- self.conf_generator.conf.get("compute",
- "image_ref_alt"))
- self.assertEqual(sorted(expected), sorted(results))
-
- def test__set_compute_images_create_fails(self):
- mock_glanceclient = mock.MagicMock()
- mock_glanceclient.images.list.return_value = []
- mock_glanceclient.images.create.side_effect = Exception()
- mock_glance = mock.MagicMock()
- mock_glance.Client.return_value = mock_glanceclient
- with mock.patch.dict("sys.modules", {"glanceclient": mock_glance}):
- self.assertRaises(config.TempestConfigCreationFailure,
- self.conf_generator._set_compute_images)
-
- def test__set_compute_ssh_connect_method_if_neutron(self):
- self.conf_generator._set_compute_ssh_connect_method()
- self.assertEqual("fixed",
- self.conf_generator.conf.get("compute",
- "ssh_connect_method"))
- # if neutron is available
- self.conf_generator.available_services = ["neutron"]
- self.conf_generator._set_compute_ssh_connect_method()
- self.assertEqual("floating",
- self.conf_generator.conf.get("compute",
- "ssh_connect_method"))
-
- def test__set_default(self):
- self.conf_generator._set_default()
+ def test__configure_default(self):
+ self.conf_generator._configure_default()
expected = (("debug", "True"), ("log_file", "tempest.log"),
("use_stderr", "False"))
results = self.conf_generator.conf.items("DEFAULT")
self.assertEqual(sorted(expected), sorted(results))
+ def test__configure_identity(self):
+ self.conf_generator._configure_identity()
+ expected = (
+ ("username", self.endpoint["username"]),
+ ("password", self.endpoint["password"]),
+ ("tenant_name", self.endpoint["tenant_name"]),
+ ("admin_username", self.endpoint["username"]),
+ ("admin_password", self.endpoint["password"]),
+ ("admin_tenant_name", self.endpoint["username"]),
+ ("admin_domain_name", self.endpoint["admin_domain_name"]),
+ ("uri", self.endpoint["auth_url"]),
+ ("uri_v3", self.endpoint["auth_url"].replace("/v2.0/", "/v3")))
+ results = self._remove_default_section(
+ self.conf_generator.conf.items("identity"))
+ self.assertEqual(sorted(expected), sorted(results))
+
+ def test__configure_option_flavor(self):
+ mock_novaclient = mock.MagicMock()
+ mock_novaclient.flavors.create.side_effect = [
+ fakes.FakeFlavor(id="id1"), fakes.FakeFlavor(id="id2")]
+ mock_nova = mock.MagicMock()
+ mock_nova.client.Client.return_value = mock_novaclient
+ self.context.conf.set("compute", "flavor_ref", "")
+ self.context.conf.set("compute", "flavor_ref_alt", "")
+ with mock.patch.dict("sys.modules", {"novaclient": mock_nova}):
+ self.context._configure_option("flavor_ref",
+ mock_novaclient.flavors.create, 64)
+ self.context._configure_option("flavor_ref_alt",
+ mock_novaclient.flavors.create, 128)
+ self.assertEqual(mock_novaclient.flavors.create.call_count, 2)
+ expected = ("id1", "id2")
+ results = (self.context.conf.get("compute", "flavor_ref"),
+ self.context.conf.get("compute", "flavor_ref_alt"))
+ self.assertEqual(sorted(expected), sorted(results))
+
+ @mock.patch("six.moves.builtins.open")
+ def test__configure_option_image(self, mock_open):
+ mock_glanceclient = mock.MagicMock()
+ mock_glanceclient.images.create.side_effect = [
+ fakes.FakeImage(id="id1"), fakes.FakeImage(id="id2")]
+ mock_glance = mock.MagicMock()
+ mock_glance.Client.return_value = mock_glanceclient
+ self.context.conf.set("compute", "image_ref", "")
+ self.context.conf.set("compute", "image_ref_alt", "")
+ with mock.patch.dict("sys.modules", {"glanceclient": mock_glance}):
+ self.context._configure_option("image_ref",
+ mock_glanceclient.images.create)
+ self.context._configure_option("image_ref_alt",
+ mock_glanceclient.images.create)
+ self.assertEqual(mock_glanceclient.images.create.call_count, 2)
+ expected = ("id1", "id2")
+ results = (self.context.conf.get("compute", "image_ref"),
+ self.context.conf.get("compute", "image_ref_alt"))
+ self.assertEqual(sorted(expected), sorted(results))
+
+ def test__configure_network_if_neutron(self):
+ fake_neutronclient = mock.MagicMock()
+ fake_neutronclient.list_networks.return_value = {
+ "networks": [
+ {
+ "status": "ACTIVE",
+ "id": "test_id",
+ "router:external": True
+ }
+ ]
+ }
+ mock_neutron = mock.MagicMock()
+ mock_neutron.client.Client.return_value = fake_neutronclient
+ with mock.patch.dict("sys.modules", {"neutronclient.neutron":
+ mock_neutron}):
+ self.conf_generator.available_services = ["neutron"]
+ self.conf_generator._configure_network()
+ expected = (("public_network_id", "test_id"),)
+ results = self._remove_default_section(
+ self.conf_generator.conf.items("network"))
+ self.assertEqual(sorted(expected), sorted(results))
+
+ def test__configure_network_if_nova(self):
+ self.conf_generator.available_services = ["nova"]
+ mock_novaclient = mock.MagicMock()
+ mock_network = mock.MagicMock()
+ mock_network.human_id = "fake-network"
+ mock_novaclient.networks.list.return_value = [mock_network]
+ mock_nova = mock.MagicMock()
+ mock_nova.client.Client.return_value = mock_novaclient
+ with mock.patch.dict("sys.modules", {"novaclient": mock_nova}):
+ self.conf_generator._configure_network()
+ self.assertEqual("fake-network",
+ self.conf_generator.conf.get(
+ "compute", "fixed_network_name"))
+ self.assertEqual("fake-network",
+ self.conf_generator.conf.get(
+ "compute", "network_for_ssh"))
+
@mock.patch("rally.verification.tempest.config.os.path.exists",
return_value=False)
@mock.patch("rally.verification.tempest.config.os.makedirs")
- def test__set_oslo_concurrency(self, mock_makedirs, mock_exists):
- self.conf_generator._set_oslo_concurrency()
- lock_path = os.path.join(self.conf_generator.data_path, "lock_files_%s"
- % self.deployment)
+ def test__configure_oslo_concurrency(self, mock_makedirs, mock_exists):
+ self.conf_generator._configure_oslo_concurrency()
+ lock_path = os.path.join(
+ self.conf_generator.data_dir, "lock_files_%s" % self.deployment)
mock_makedirs.assert_called_once_with(lock_path)
expected = (("lock_path", lock_path),)
results = self._remove_default_section(
self.conf_generator.conf.items("oslo_concurrency"))
self.assertEqual(sorted(expected), sorted(results))
- def test__set_identity(self):
- self.conf_generator._set_identity()
- expected = (("username", self.endpoint["username"]),
- ("password", self.endpoint["password"]),
- ("tenant_name", self.endpoint["tenant_name"]),
- ("alt_username", self.endpoint["username"]),
- ("alt_password", self.endpoint["password"]),
- ("alt_tenant_name", self.endpoint["tenant_name"]),
- ("admin_username", self.endpoint["username"]),
- ("admin_password", self.endpoint["password"]),
- ("admin_tenant_name", self.endpoint["username"]),
- ("admin_domain_name", self.endpoint["admin_domain_name"]),
- ("uri", self.endpoint["auth_url"]),
- ("uri_v3", self.endpoint["auth_url"].replace("/v2.0",
- "/v3")))
- results = self._remove_default_section(
- self.conf_generator.conf.items("identity"))
- self.assertEqual(sorted(expected), sorted(results))
-
- def test__set_network_if_neutron(self):
- fake_neutronclient = mock.MagicMock()
- fake_neutronclient.list_networks.return_value = {"networks": [
- {"status": "ACTIVE",
- "id": "test_id",
- "router:external":
- True}]}
- fake_neutronclient.list_routers.return_value = {"routers": [
- {"id": "test_router"}]}
- fake_neutronclient.list_subnets.return_value = {"subnets": [
- {"cidr":
- "10.0.0.0/24"}]}
- mock_neutron = mock.MagicMock()
- mock_neutron.client.Client.return_value = fake_neutronclient
- with mock.patch.dict("sys.modules", {"neutronclient.neutron":
- mock_neutron}):
- self.conf_generator.available_services = ["neutron"]
- self.conf_generator._set_network()
- expected = (("tenant_network_cidr", "10.0.0.0/24"),
- ("tenant_networks_reachable", "False"),
- ("public_network_id", "test_id"),
- ("public_router_id", "test_router"))
- results = self._remove_default_section(
- self.conf_generator.conf.items("network"))
- self.assertEqual(sorted(expected), sorted(results))
-
- def test__set_network_if_nova(self):
- network = "10.0.0.0/24"
- mock_novaclient = mock.MagicMock()
- mock_network = mock.MagicMock()
- mock_network.cidr = network
- mock_novaclient.networks.list.return_value = [mock_network]
- mock_nova = mock.MagicMock()
- mock_nova.client.Client.return_value = mock_novaclient
- with mock.patch.dict("sys.modules", {"novaclient": mock_nova}):
- self.conf_generator._set_network()
- self.assertEqual(network,
- self.conf_generator.conf.get(
- "network", "tenant_network_cidr"))
-
@mock.patch("rally.verification.tempest.config.requests")
- def test__set_service_available(self, mock_requests):
+ def test__configure_service_available(self, mock_requests):
mock_result = mock.MagicMock()
mock_result.status_code = 404
mock_requests.get.return_value = mock_result
- available_services = ("nova", "cinder", "glance")
+ available_services = ("nova", "cinder", "glance", "sahara")
self.conf_generator.available_services = available_services
- self.conf_generator._set_service_available()
+ self.conf_generator._configure_service_available()
expected = (("neutron", "False"), ("heat", "False"),
("ceilometer", "False"), ("swift", "False"),
("cinder", "True"), ("nova", "True"),
- ("glance", "True"), ("horizon", "False"))
+ ("glance", "True"), ("horizon", "False"),
+ ("sahara", "True"))
options = self._remove_default_section(
self.conf_generator.conf.items("service_available"))
self.assertEqual(sorted(expected), sorted(options))
@mock.patch("rally.verification.tempest.config.requests")
- def test__set_service_available_horizon(self, mock_requests):
+ def test__configure_service_available_horizon(self, mock_requests):
mock_result = mock.MagicMock()
mock_result.status_code = 200
mock_requests.get.return_value = mock_result
- self.conf_generator._set_service_available()
- self.assertEqual(self.conf_generator.conf.get(
- "service_available", "horizon"), "True")
+ self.conf_generator._configure_service_available()
+ self.assertEqual(
+ self.conf_generator.conf.get(
+ "service_available", "horizon"), "True")
@mock.patch("rally.verification.tempest.config.requests.get")
- def test__set_service_not_available_horizon(self, mock_get):
+ def test__configure_service_not_available_horizon(self, mock_get):
mock_get.side_effect = requests.Timeout()
- self.conf_generator._set_service_available()
- self.assertEqual(self.conf_generator.conf.get(
- "service_available", "horizon"), "False")
+ self.conf_generator._configure_service_available()
+ self.assertEqual(
+ self.conf_generator.conf.get(
+ "service_available", "horizon"), "False")
- @mock.patch("six.moves.builtins.open", side_effect=mock.mock_open(),
- create=True)
- def test_write_config(self, mock_open):
- self.conf_generator.conf = mock.Mock()
- file_name = "/path/to/fake/conf"
+ def test__configure_validation_if_neutron(self):
+ # if neutron is available
+ self.conf_generator.available_services = ["neutron"]
+ self.conf_generator._configure_validation()
+ self.assertEqual("floating",
+ self.conf_generator.conf.get("validation",
+ "connect_method"))
- self.conf_generator.write_config(file_name)
+ def test__configure_validation_if_novanetwork(self):
+ self.conf_generator._configure_validation()
+ self.assertEqual("fixed",
+ self.conf_generator.conf.get("validation",
+ "connect_method"))
- mock_open.assert_called_once_with(file_name, "w+")
- self.conf_generator.conf.write.assert_called_once_with(
- mock_open.side_effect())
+ @mock.patch("six.moves.builtins.open",
+ side_effect=mock.mock_open(), create=True)
+ def test__write_config(self, mock_open):
+ conf_path = "/path/to/fake/conf"
+ conf_data = mock.Mock()
+ config._write_config(conf_path, conf_data)
+ mock_open.assert_called_once_with(conf_path, "w+")
+ conf_data.write.assert_called_once_with(mock_open.side_effect())
diff --git a/tests/unit/verification/test_tempest.py b/tests/unit/verification/test_tempest.py
index e0a93c399b..4df590e71a 100644
--- a/tests/unit/verification/test_tempest.py
+++ b/tests/unit/verification/test_tempest.py
@@ -339,12 +339,14 @@ class TempestVerifyTestCase(BaseTestCase):
return_value=(None, None))
@mock.patch(TEMPEST_PATH + ".tempest.Tempest.env")
@mock.patch(TEMPEST_PATH + ".tempest.subprocess")
- @mock.patch(TEMPEST_PATH + ".config.TempestConf")
+ @mock.patch(TEMPEST_PATH + ".config.TempestResourcesContext")
+ @mock.patch(TEMPEST_PATH + ".config.TempestConfig")
@mock.patch(TEMPEST_PATH + ".tempest.Tempest.is_configured",
return_value=False)
def test_verify_not_configured(
- self, mock_tempest_is_configured, mock_tempest_conf,
- mock_subprocess, mock_tempest_env, mock_tempest_parse_results):
+ self, mock_tempest_is_configured, mock_tempest_config,
+ mock_tempest_resources_context, mock_subprocess, mock_tempest_env,
+ mock_tempest_parse_results):
set_name = "compute"
fake_call = self._get_fake_call(set_name)
@@ -352,8 +354,8 @@ class TempestVerifyTestCase(BaseTestCase):
self.verifier.verify(set_name, None)
self.assertEqual(2, mock_tempest_is_configured.call_count)
- mock_tempest_conf.assert_called_once_with(self.verifier.deployment)
- mock_tempest_conf.return_value.generate.assert_called_once_with(
+ mock_tempest_config.assert_called_once_with(self.verifier.deployment)
+ mock_tempest_config.return_value.generate.assert_called_once_with(
self.verifier.config_file
)
self.verifier.verification.start_verifying.assert_called_once_with(
@@ -368,20 +370,22 @@ class TempestVerifyTestCase(BaseTestCase):
return_value=(None, None))
@mock.patch(TEMPEST_PATH + ".tempest.Tempest.env")
@mock.patch(TEMPEST_PATH + ".tempest.subprocess")
- @mock.patch(TEMPEST_PATH + ".config.TempestConf")
+ @mock.patch(TEMPEST_PATH + ".config.TempestResourcesContext")
+ @mock.patch(TEMPEST_PATH + ".config.TempestConfig")
@mock.patch(TEMPEST_PATH + ".tempest.Tempest.is_configured",
return_value=True)
def test_verify_when_tempest_configured(
- self, mock_tempest_is_configured, mock_tempest_conf,
- mock_subprocess, mock_tempest_env, mock_tempest_parse_results):
+ self, mock_tempest_is_configured, mock_tempest_config,
+ mock_tempest_resources_context, mock_subprocess, mock_tempest_env,
+ mock_tempest_parse_results):
set_name = "identity"
fake_call = self._get_fake_call(set_name)
self.verifier.verify(set_name, None)
mock_tempest_is_configured.assert_called_once_with()
- self.assertFalse(mock_tempest_conf.called)
- self.assertFalse(mock_tempest_conf().generate.called)
+ self.assertFalse(mock_tempest_config.called)
+ self.assertFalse(mock_tempest_config().generate.called)
self.verifier.verification.start_verifying.assert_called_once_with(
set_name)
@@ -394,12 +398,14 @@ class TempestVerifyTestCase(BaseTestCase):
return_value=(None, None))
@mock.patch(TEMPEST_PATH + ".tempest.Tempest.env")
@mock.patch(TEMPEST_PATH + ".tempest.subprocess")
- @mock.patch(TEMPEST_PATH + ".config.TempestConf")
+ @mock.patch(TEMPEST_PATH + ".config.TempestResourcesContext")
+ @mock.patch(TEMPEST_PATH + ".config.TempestConfig")
@mock.patch(TEMPEST_PATH + ".tempest.Tempest.is_configured",
return_value=True)
def test_verify_failed_and_tempest_is_configured(
- self, mock_tempest_is_configured, mock_tempest_conf,
- mock_subprocess, mock_tempest_env, mock_tempest_parse_results):
+ self, mock_tempest_is_configured, mock_tempest_config,
+ mock_tempest_resources_context, mock_subprocess, mock_tempest_env,
+ mock_tempest_parse_results):
set_name = "identity"
fake_call = self._get_fake_call(set_name)
mock_subprocess.side_effect = subprocess.CalledProcessError
@@ -407,8 +413,8 @@ class TempestVerifyTestCase(BaseTestCase):
self.verifier.verify(set_name, None)
mock_tempest_is_configured.assert_called_once_with()
- self.assertFalse(mock_tempest_conf.called)
- self.assertFalse(mock_tempest_conf().generate.called)
+ self.assertFalse(mock_tempest_config.called)
+ self.assertFalse(mock_tempest_config().generate.called)
self.verifier.verification.start_verifying.assert_called_once_with(
set_name)